@gblp/circle-of-fifths 0.2.0 → 0.3.1

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
@@ -2,6 +2,12 @@
2
2
 
3
3
  Standalone Angular 22 component for exploring keys, diatonic chords, and common progressions with an interactive circle of fifths.
4
4
 
5
+ [View the package on npm](https://www.npmjs.com/package/@gblp/circle-of-fifths)
6
+
7
+ ```bash
8
+ npm install @gblp/circle-of-fifths
9
+ ```
10
+
5
11
  ```ts
6
12
  import { Component } from '@angular/core';
7
13
  import { CircleOfFifthsComponent } from '@gblp/circle-of-fifths';
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, computed, signal, Component } from '@angular/core';
2
+ import { input, computed, Component, signal } from '@angular/core';
3
3
  import { NgClass } from '@angular/common';
4
4
 
5
5
  const PROGRESSIONS = {
@@ -28,6 +28,30 @@ const PROGRESSIONS = {
28
28
  genre: { en: 'Film, Jazz', es: 'Cine, jazz' },
29
29
  numerals: ['ii', 'V', 'vii°', 'I'],
30
30
  },
31
+ {
32
+ name: { en: 'Nostalgic', es: 'Retro' },
33
+ mood: { en: '🕺 Retro', es: '🕺 Retro' },
34
+ genre: { en: 'Doo-Wop, Pop', es: 'Doo-Wop, pop' },
35
+ numerals: ['I', 'vi', 'IV', 'V'],
36
+ },
37
+ {
38
+ name: { en: 'Dreamy', es: 'Soñadora' },
39
+ mood: { en: '🌸 Floaty', es: '🌸 Etérea' },
40
+ genre: { en: 'J-Pop, Anime', es: 'J-Pop, anime' },
41
+ numerals: ['IV', 'V', 'iii', 'vi'],
42
+ },
43
+ {
44
+ name: { en: 'Gospel', es: 'Gospel' },
45
+ mood: { en: '🙏 Uplifting', es: '🙏 Elevadora' },
46
+ genre: { en: 'Gospel, Soul', es: 'Gospel, soul' },
47
+ numerals: ['I', 'IV', 'ii', 'V'],
48
+ },
49
+ {
50
+ name: { en: 'Bittersweet', es: 'Agridulce' },
51
+ mood: { en: '🍂 Bittersweet', es: '🍂 Agridulce' },
52
+ genre: { en: 'Alt Rock, Indie', es: 'Rock alternativo, indie' },
53
+ numerals: ['I', 'iii', 'IV', 'V'],
54
+ },
31
55
  ],
32
56
  minor: [
33
57
  {
@@ -54,6 +78,30 @@ const PROGRESSIONS = {
54
78
  genre: { en: 'Cinematic', es: 'Cinemática' },
55
79
  numerals: ['i', 'v', 'VI', 'VII'],
56
80
  },
81
+ {
82
+ name: { en: 'Flamenco', es: 'Flamenco' },
83
+ mood: { en: '🌹 Passionate', es: '🌹 Apasionado' },
84
+ genre: { en: 'Flamenco, World', es: 'Flamenco, mundial' },
85
+ numerals: ['i', 'VII', 'VI', 'iv'],
86
+ },
87
+ {
88
+ name: { en: 'Tragic', es: 'Trágica' },
89
+ mood: { en: '💔 Tragic', es: '💔 Trágica' },
90
+ genre: { en: 'Classical, Drama', es: 'Clásica, drama' },
91
+ numerals: ['i', 'iv', 'v', 'i'],
92
+ },
93
+ {
94
+ name: { en: 'Epic', es: 'Épica' },
95
+ mood: { en: '⚔️ Heroic', es: '⚔️ Heroico' },
96
+ genre: { en: 'Epic, Orchestral', es: 'Épica, orquestal' },
97
+ numerals: ['i', 'III', 'VII', 'VI'],
98
+ },
99
+ {
100
+ name: { en: 'Ethereal', es: 'Etérea' },
101
+ mood: { en: '🌊 Hypnotic', es: '🌊 Hipnótica' },
102
+ genre: { en: 'Ambient, Post-Rock', es: 'Ambient, post-rock' },
103
+ numerals: ['i', 'VI', 'iv', 'VII'],
104
+ },
57
105
  ],
58
106
  };
59
107
  const COPY = {
@@ -70,6 +118,14 @@ const COPY = {
70
118
  diminishedLegend: 'Diminished (vii° / ii°)',
71
119
  diatonicChords: 'Diatonic Chords',
72
120
  commonProgressions: 'Common Progressions in',
121
+ generateProgressions: 'Shuffle',
122
+ chorus: 'Chorus',
123
+ bridge: 'Bridge',
124
+ outro: 'Outro',
125
+ verse: 'Verse',
126
+ copy: 'Copy',
127
+ expand: 'Expand',
128
+ collapse: 'Collapse',
73
129
  major: 'Major',
74
130
  minor: 'Minor',
75
131
  diminished: 'diminished',
@@ -99,6 +155,14 @@ const COPY = {
99
155
  diminishedLegend: 'Disminuido (vii° / ii°)',
100
156
  diatonicChords: 'Acordes diatónicos',
101
157
  commonProgressions: 'Progresiones comunes en',
158
+ generateProgressions: 'Generar nuevos',
159
+ chorus: 'Coro',
160
+ bridge: 'Puente',
161
+ outro: 'Final',
162
+ verse: 'Estrofa',
163
+ copy: 'Copiar',
164
+ expand: 'Expandir',
165
+ collapse: 'Colapsar',
102
166
  major: 'Mayor',
103
167
  minor: 'Menor',
104
168
  diminished: 'disminuido',
@@ -116,6 +180,102 @@ const COPY = {
116
180
  },
117
181
  },
118
182
  };
183
+ const KEYS = [
184
+ { index: 0, major: 'C', minor: 'Am', sharps: 0, flats: 0 },
185
+ { index: 1, major: 'G', minor: 'Em', sharps: 1, flats: 0 },
186
+ { index: 2, major: 'D', minor: 'Bm', sharps: 2, flats: 0 },
187
+ { index: 3, major: 'A', minor: 'F♯m', sharps: 3, flats: 0 },
188
+ { index: 4, major: 'E', minor: 'C♯m', sharps: 4, flats: 0 },
189
+ { index: 5, major: 'B', minor: 'G♯m', sharps: 5, flats: 0 },
190
+ { index: 6, major: 'F♯', minor: 'D♯m', sharps: 6, flats: 0 },
191
+ { index: 7, major: 'D♭', minor: 'B♭m', sharps: 0, flats: 5 },
192
+ { index: 8, major: 'A♭', minor: 'Fm', sharps: 0, flats: 4 },
193
+ { index: 9, major: 'E♭', minor: 'Cm', sharps: 0, flats: 3 },
194
+ { index: 10, major: 'B♭', minor: 'Gm', sharps: 0, flats: 2 },
195
+ { index: 11, major: 'F', minor: 'Dm', sharps: 0, flats: 1 },
196
+ ];
197
+ function dimChord(idx) {
198
+ // The diminished chord root is the minor key two steps clockwise (idx+2)
199
+ return KEYS[(idx + 2) % 12].minor.replace(/m$/, '°');
200
+ }
201
+ function buildChordRows(idx, type, copy) {
202
+ const prev = (idx - 1 + 12) % 12;
203
+ const next = (idx + 1) % 12;
204
+ if (type === 'major') {
205
+ return [
206
+ { numeral: 'I', chord: KEYS[idx].major, chordType: copy.major, role: copy.roles.tonic, state: 'tonic' },
207
+ { numeral: 'ii', chord: KEYS[prev].minor, chordType: copy.minor, role: copy.roles.supertonic, state: 'scale-minor' },
208
+ { numeral: 'iii', chord: KEYS[next].minor, chordType: copy.minor, role: copy.roles.mediant, state: 'scale-minor' },
209
+ { numeral: 'IV', chord: KEYS[prev].major, chordType: copy.major, role: copy.roles.subdominant, state: 'scale-major' },
210
+ { numeral: 'V', chord: KEYS[next].major, chordType: copy.major, role: copy.roles.dominant, state: 'scale-major' },
211
+ { numeral: 'vi', chord: KEYS[idx].minor, chordType: copy.minor, role: copy.roles.submediant, state: 'scale-minor' },
212
+ { numeral: 'vii°', chord: dimChord(idx), chordType: copy.diminished, role: copy.roles.leadingTone, state: 'scale-diminished' },
213
+ ];
214
+ }
215
+ else {
216
+ return [
217
+ { numeral: 'i', chord: KEYS[idx].minor, chordType: copy.minor, role: copy.roles.tonic, state: 'tonic' },
218
+ { numeral: 'iv', chord: KEYS[prev].minor, chordType: copy.minor, role: copy.roles.subdominant, state: 'scale-minor' },
219
+ { numeral: 'v', chord: KEYS[next].minor, chordType: copy.minor, role: copy.roles.dominant, state: 'scale-minor' },
220
+ { numeral: 'III', chord: KEYS[idx].major, chordType: copy.major, role: copy.roles.mediant, state: 'scale-major' },
221
+ { numeral: 'VI', chord: KEYS[prev].major, chordType: copy.major, role: copy.roles.submediant, state: 'scale-major' },
222
+ { numeral: 'VII', chord: KEYS[next].major, chordType: copy.major, role: copy.roles.subtonic, state: 'scale-major' },
223
+ { numeral: 'ii°', chord: dimChord(idx), chordType: copy.diminished, role: copy.roles.supertonic, state: 'scale-diminished' },
224
+ ];
225
+ }
226
+ }
227
+ class ChordSectionComponent {
228
+ chords = input([], /* @ts-ignore */
229
+ ...(ngDevMode ? [{ debugName: "chords" }] : /* istanbul ignore next */ []));
230
+ language = input('en', /* @ts-ignore */
231
+ ...(ngDevMode ? [{ debugName: "language" }] : /* istanbul ignore next */ []));
232
+ text = computed(() => COPY[this.language()], /* @ts-ignore */
233
+ ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
234
+ match = computed(() => {
235
+ const input = this.chords();
236
+ if (!input.length)
237
+ return null;
238
+ const inputSet = new Set(input);
239
+ let best = null;
240
+ for (let idx = 0; idx < 12; idx++) {
241
+ for (const type of ['major', 'minor']) {
242
+ // chord names are language-independent; COPY.en is a dummy for label fields
243
+ const score = buildChordRows(idx, type, COPY.en).filter((r) => inputSet.has(r.chord)).length;
244
+ if (!best || score > best.score)
245
+ best = { idx, type, score };
246
+ }
247
+ }
248
+ return best && best.score > 0 ? best : null;
249
+ }, /* @ts-ignore */
250
+ ...(ngDevMode ? [{ debugName: "match" }] : /* istanbul ignore next */ []));
251
+ rows = computed(() => {
252
+ const m = this.match();
253
+ if (!m)
254
+ return [];
255
+ return buildChordRows(m.idx, m.type, this.text());
256
+ }, /* @ts-ignore */
257
+ ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
258
+ info = computed(() => {
259
+ const m = this.match();
260
+ if (!m)
261
+ return null;
262
+ const copy = this.text();
263
+ const key = KEYS[m.idx];
264
+ return {
265
+ fullName: `${m.type === 'major' ? key.major : key.minor} ${m.type === 'major' ? copy.major : copy.minor}`,
266
+ relativeKey: m.type === 'major'
267
+ ? `${copy.relativeMinor}: ${key.minor}`
268
+ : `${copy.relativeMajor}: ${key.major}`,
269
+ };
270
+ }, /* @ts-ignore */
271
+ ...(ngDevMode ? [{ debugName: "info" }] : /* istanbul ignore next */ []));
272
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ChordSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
273
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: ChordSectionComponent, isStandalone: true, selector: "chord-section", inputs: { chords: { classPropertyName: "chords", publicName: "chords", isSignal: true, isRequired: false, transformFunction: null }, language: { classPropertyName: "language", publicName: "language", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (rows().length) {\n <section class=\"chord-section\">\n @if (info(); as info) {\n <h2>{{ info.fullName }} \u2014 {{ text().diatonicChords }}</h2>\n <p class=\"relative-label\">{{ info.relativeKey }}</p>\n }\n <div class=\"chord-grid\">\n @for (row of rows(); track row.numeral) {\n <div class=\"chord-card\" [ngClass]=\"row.state\">\n <div class=\"chord-numeral\">{{ row.numeral }}</div>\n <div class=\"chord-name\">{{ row.chord }}</div>\n <div class=\"chord-type\">{{ row.chordType }}</div>\n <div class=\"chord-role\">{{ row.role }}</div>\n </div>\n }\n </div>\n </section>\n}\n", styles: [":host{--_chords-background: var(--chords-background, #f0f4f8);--_chords-surface: var(--chords-surface, #ffffff);--_chords-surface-elevated: var(--chords-surface-elevated, #f8fafc);--_chords-text: var(--chords-text, #1e293b);--_chords-muted: var(--chords-muted, #64748b);--_chords-primary: var(--chords-primary, #3b82f6);--_chords-secondary: var(--chords-secondary, #06b6d4);--_chords-highlight: var(--chords-highlight, #f59e0b);--_chords-danger: var(--chords-danger, #a855f7);--_chords-border: var(--chords-border, #e2e8f0);--_chords-on-primary: var(--chords-on-primary, #ffffff);display:block;font-family:Segoe UI,system-ui,sans-serif}.chord-section{margin-top:36px;text-align:center}.chord-section h2{margin:0 0 4px;font-size:1.25rem;font-weight:700;color:var(--_chords-text)}.chord-section .relative-label{margin:0 0 20px;font-size:.88rem;color:var(--_chords-muted)}.chord-grid{display:flex;flex-wrap:nowrap;gap:8px;justify-content:center;overflow-x:auto;padding-bottom:4px}.chord-card{border-radius:12px;padding:12px 14px;min-width:82px;flex-shrink:0;text-align:center;background:color-mix(in srgb,var(--_chords-text) 8%,var(--_chords-surface));border:2px solid transparent;transition:transform .15s ease,box-shadow .15s ease}.chord-card:hover{transform:translateY(-2px);box-shadow:0 4px 16px color-mix(in srgb,var(--_chords-text) 10%,transparent)}.chord-card .chord-numeral{font-size:.85rem;font-weight:700;font-style:italic;color:var(--_chords-muted)}.chord-card .chord-name{font-size:1.4rem;font-weight:800;color:var(--_chords-text);margin:2px 0;letter-spacing:-.02em}.chord-card .chord-type{font-size:.65rem;text-transform:uppercase;letter-spacing:.06em;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.chord-card .chord-role{font-size:.7rem;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background));margin-top:2px}.chord-card.tonic{background:color-mix(in srgb,var(--_chords-highlight) 10%,var(--_chords-surface));border-color:var(--_chords-highlight)}.chord-card.tonic .chord-numeral,.chord-card.tonic .chord-name{color:color-mix(in srgb,var(--_chords-highlight) 58%,var(--_chords-text))}.chord-card.scale-major{background:color-mix(in srgb,var(--_chords-primary) 9%,var(--_chords-surface));border-color:var(--_chords-primary)}.chord-card.scale-major .chord-numeral,.chord-card.scale-major .chord-name{color:color-mix(in srgb,var(--_chords-primary) 65%,var(--_chords-text))}.chord-card.scale-diminished{background:color-mix(in srgb,var(--_chords-danger) 9%,var(--_chords-surface));border-color:var(--_chords-danger)}.chord-card.scale-diminished .chord-numeral,.chord-card.scale-diminished .chord-name{color:color-mix(in srgb,var(--_chords-danger) 65%,var(--_chords-text))}@media(max-width:480px){.chord-card{min-width:75px;padding:10px 12px}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
274
+ }
275
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ChordSectionComponent, decorators: [{
276
+ type: Component,
277
+ args: [{ selector: 'chord-section', standalone: true, imports: [NgClass], template: "@if (rows().length) {\n <section class=\"chord-section\">\n @if (info(); as info) {\n <h2>{{ info.fullName }} \u2014 {{ text().diatonicChords }}</h2>\n <p class=\"relative-label\">{{ info.relativeKey }}</p>\n }\n <div class=\"chord-grid\">\n @for (row of rows(); track row.numeral) {\n <div class=\"chord-card\" [ngClass]=\"row.state\">\n <div class=\"chord-numeral\">{{ row.numeral }}</div>\n <div class=\"chord-name\">{{ row.chord }}</div>\n <div class=\"chord-type\">{{ row.chordType }}</div>\n <div class=\"chord-role\">{{ row.role }}</div>\n </div>\n }\n </div>\n </section>\n}\n", styles: [":host{--_chords-background: var(--chords-background, #f0f4f8);--_chords-surface: var(--chords-surface, #ffffff);--_chords-surface-elevated: var(--chords-surface-elevated, #f8fafc);--_chords-text: var(--chords-text, #1e293b);--_chords-muted: var(--chords-muted, #64748b);--_chords-primary: var(--chords-primary, #3b82f6);--_chords-secondary: var(--chords-secondary, #06b6d4);--_chords-highlight: var(--chords-highlight, #f59e0b);--_chords-danger: var(--chords-danger, #a855f7);--_chords-border: var(--chords-border, #e2e8f0);--_chords-on-primary: var(--chords-on-primary, #ffffff);display:block;font-family:Segoe UI,system-ui,sans-serif}.chord-section{margin-top:36px;text-align:center}.chord-section h2{margin:0 0 4px;font-size:1.25rem;font-weight:700;color:var(--_chords-text)}.chord-section .relative-label{margin:0 0 20px;font-size:.88rem;color:var(--_chords-muted)}.chord-grid{display:flex;flex-wrap:nowrap;gap:8px;justify-content:center;overflow-x:auto;padding-bottom:4px}.chord-card{border-radius:12px;padding:12px 14px;min-width:82px;flex-shrink:0;text-align:center;background:color-mix(in srgb,var(--_chords-text) 8%,var(--_chords-surface));border:2px solid transparent;transition:transform .15s ease,box-shadow .15s ease}.chord-card:hover{transform:translateY(-2px);box-shadow:0 4px 16px color-mix(in srgb,var(--_chords-text) 10%,transparent)}.chord-card .chord-numeral{font-size:.85rem;font-weight:700;font-style:italic;color:var(--_chords-muted)}.chord-card .chord-name{font-size:1.4rem;font-weight:800;color:var(--_chords-text);margin:2px 0;letter-spacing:-.02em}.chord-card .chord-type{font-size:.65rem;text-transform:uppercase;letter-spacing:.06em;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.chord-card .chord-role{font-size:.7rem;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background));margin-top:2px}.chord-card.tonic{background:color-mix(in srgb,var(--_chords-highlight) 10%,var(--_chords-surface));border-color:var(--_chords-highlight)}.chord-card.tonic .chord-numeral,.chord-card.tonic .chord-name{color:color-mix(in srgb,var(--_chords-highlight) 58%,var(--_chords-text))}.chord-card.scale-major{background:color-mix(in srgb,var(--_chords-primary) 9%,var(--_chords-surface));border-color:var(--_chords-primary)}.chord-card.scale-major .chord-numeral,.chord-card.scale-major .chord-name{color:color-mix(in srgb,var(--_chords-primary) 65%,var(--_chords-text))}.chord-card.scale-diminished{background:color-mix(in srgb,var(--_chords-danger) 9%,var(--_chords-surface));border-color:var(--_chords-danger)}.chord-card.scale-diminished .chord-numeral,.chord-card.scale-diminished .chord-name{color:color-mix(in srgb,var(--_chords-danger) 65%,var(--_chords-text))}@media(max-width:480px){.chord-card{min-width:75px;padding:10px 12px}}\n"] }]
278
+ }], propDecorators: { chords: [{ type: i0.Input, args: [{ isSignal: true, alias: "chords", required: false }] }], language: [{ type: i0.Input, args: [{ isSignal: true, alias: "language", required: false }] }] } });
119
279
  class CircleOfFifthsComponent {
120
280
  language = input('en', /* @ts-ignore */
121
281
  ...(ngDevMode ? [{ debugName: "language" }] : /* istanbul ignore next */ []));
@@ -126,24 +286,15 @@ class CircleOfFifthsComponent {
126
286
  KEY_SIG = { inner: 250, outer: 288 };
127
287
  MAJOR = { inner: 172, outer: 250 };
128
288
  MINOR = { inner: 102, outer: 172 };
129
- keys = [
130
- { index: 0, major: 'C', minor: 'Am', sharps: 0, flats: 0 },
131
- { index: 1, major: 'G', minor: 'Em', sharps: 1, flats: 0 },
132
- { index: 2, major: 'D', minor: 'Bm', sharps: 2, flats: 0 },
133
- { index: 3, major: 'A', minor: 'F♯m', sharps: 3, flats: 0 },
134
- { index: 4, major: 'E', minor: 'C♯m', sharps: 4, flats: 0 },
135
- { index: 5, major: 'B', minor: 'G♯m', sharps: 5, flats: 0 },
136
- { index: 6, major: 'F♯', minor: 'D♯m', sharps: 6, flats: 0 },
137
- { index: 7, major: 'D♭', minor: 'B♭m', sharps: 0, flats: 5 },
138
- { index: 8, major: 'A♭', minor: 'Fm', sharps: 0, flats: 4 },
139
- { index: 9, major: 'E♭', minor: 'Cm', sharps: 0, flats: 3 },
140
- { index: 10, major: 'B♭', minor: 'Gm', sharps: 0, flats: 2 },
141
- { index: 11, major: 'F', minor: 'Dm', sharps: 0, flats: 1 },
142
- ];
289
+ keys = KEYS;
143
290
  selectedIndex = signal(null, /* @ts-ignore */
144
291
  ...(ngDevMode ? [{ debugName: "selectedIndex" }] : /* istanbul ignore next */ []));
145
292
  selectedType = signal(null, /* @ts-ignore */
146
293
  ...(ngDevMode ? [{ debugName: "selectedType" }] : /* istanbul ignore next */ []));
294
+ activeProgressionDefs = signal(null, /* @ts-ignore */
295
+ ...(ngDevMode ? [{ debugName: "activeProgressionDefs" }] : /* istanbul ignore next */ []));
296
+ expandedCards = signal(new Set(), /* @ts-ignore */
297
+ ...(ngDevMode ? [{ debugName: "expandedCards" }] : /* istanbul ignore next */ []));
147
298
  get slice() {
148
299
  const idx = this.selectedIndex();
149
300
  if (idx === null)
@@ -163,6 +314,29 @@ class CircleOfFifthsComponent {
163
314
  this.selectedIndex.set(index);
164
315
  this.selectedType.set(type);
165
316
  }
317
+ this.activeProgressionDefs.set(null);
318
+ this.expandedCards.set(new Set());
319
+ }
320
+ randomizeProgressions() {
321
+ const type = this.selectedType();
322
+ if (!type)
323
+ return;
324
+ // ponytail: sort-shuffle is fine for UI randomness
325
+ this.activeProgressionDefs.set([...PROGRESSIONS[type]].sort(() => Math.random() - 0.5).slice(0, 4));
326
+ this.expandedCards.set(new Set());
327
+ }
328
+ toggleCard(name) {
329
+ this.expandedCards.update((set) => {
330
+ const next = new Set(set);
331
+ next.has(name) ? next.delete(name) : next.add(name);
332
+ return next;
333
+ });
334
+ }
335
+ copyChords(prog) {
336
+ const text = this.expandedCards().has(prog.name)
337
+ ? [`${this.text().verse}: ${prog.chords.join(', ')}`, ...prog.sections.map((s) => `${s.label}: ${s.chords.join(', ')}`)].join('; ')
338
+ : prog.chords.join(', ');
339
+ navigator.clipboard.writeText(text);
166
340
  }
167
341
  getMajorState(index) {
168
342
  const s = this.slice;
@@ -259,10 +433,15 @@ class CircleOfFifthsComponent {
259
433
  y: this.CY + midR * Math.sin(a),
260
434
  };
261
435
  }
262
- dimChord(idx) {
263
- // The diminished chord root is the minor key two steps clockwise (idx+2)
264
- const minor = this.keys[(idx + 2) % 12].minor;
265
- return minor.replace(/m$/, '°');
436
+ get chordTable() {
437
+ const idx = this.selectedIndex();
438
+ const type = this.selectedType();
439
+ if (idx === null || type === null)
440
+ return [];
441
+ return buildChordRows(idx, type, this.text());
442
+ }
443
+ get chordNames() {
444
+ return this.chordTable.map((r) => r.chord);
266
445
  }
267
446
  getAccidentalText(key) {
268
447
  if (key.sharps > 0)
@@ -276,7 +455,7 @@ class CircleOfFifthsComponent {
276
455
  const type = this.selectedType();
277
456
  if (idx === null || type === null)
278
457
  return null;
279
- const key = this.keys[idx];
458
+ const key = KEYS[idx];
280
459
  const copy = this.text();
281
460
  const scale = type === 'major' ? copy.major : copy.minor;
282
461
  return {
@@ -288,120 +467,29 @@ class CircleOfFifthsComponent {
288
467
  : `${copy.relativeMajor}: ${key.major}`,
289
468
  };
290
469
  }
291
- get chordTable() {
292
- const idx = this.selectedIndex();
470
+ deriveSections(numerals, lookup) {
293
471
  const type = this.selectedType();
294
- if (idx === null || type === null)
295
- return [];
296
- const prev = (idx - 1 + 12) % 12;
297
- const next = (idx + 1) % 12;
298
472
  const copy = this.text();
299
- if (type === 'major') {
300
- return [
301
- {
302
- numeral: 'I',
303
- chord: this.keys[idx].major,
304
- chordType: copy.major,
305
- role: copy.roles.tonic,
306
- state: 'tonic',
307
- },
308
- {
309
- numeral: 'ii',
310
- chord: this.keys[prev].minor,
311
- chordType: copy.minor,
312
- role: copy.roles.supertonic,
313
- state: 'scale-minor',
314
- },
315
- {
316
- numeral: 'iii',
317
- chord: this.keys[next].minor,
318
- chordType: copy.minor,
319
- role: copy.roles.mediant,
320
- state: 'scale-minor',
321
- },
322
- {
323
- numeral: 'IV',
324
- chord: this.keys[prev].major,
325
- chordType: copy.major,
326
- role: copy.roles.subdominant,
327
- state: 'scale-major',
328
- },
329
- {
330
- numeral: 'V',
331
- chord: this.keys[next].major,
332
- chordType: copy.major,
333
- role: copy.roles.dominant,
334
- state: 'scale-major',
335
- },
336
- {
337
- numeral: 'vi',
338
- chord: this.keys[idx].minor,
339
- chordType: copy.minor,
340
- role: copy.roles.submediant,
341
- state: 'scale-minor',
342
- },
343
- {
344
- numeral: 'vii°',
345
- chord: this.dimChord(idx),
346
- chordType: copy.diminished,
347
- role: copy.roles.leadingTone,
348
- state: 'scale-diminished',
349
- },
350
- ];
351
- }
352
- else {
353
- return [
354
- {
355
- numeral: 'i',
356
- chord: this.keys[idx].minor,
357
- chordType: copy.minor,
358
- role: copy.roles.tonic,
359
- state: 'tonic',
360
- },
361
- {
362
- numeral: 'iv',
363
- chord: this.keys[prev].minor,
364
- chordType: copy.minor,
365
- role: copy.roles.subdominant,
366
- state: 'scale-minor',
367
- },
368
- {
369
- numeral: 'v',
370
- chord: this.keys[next].minor,
371
- chordType: copy.minor,
372
- role: copy.roles.dominant,
373
- state: 'scale-minor',
374
- },
375
- {
376
- numeral: 'III',
377
- chord: this.keys[idx].major,
378
- chordType: copy.major,
379
- role: copy.roles.mediant,
380
- state: 'scale-major',
381
- },
382
- {
383
- numeral: 'VI',
384
- chord: this.keys[prev].major,
385
- chordType: copy.major,
386
- role: copy.roles.submediant,
387
- state: 'scale-major',
388
- },
389
- {
390
- numeral: 'VII',
391
- chord: this.keys[next].major,
392
- chordType: copy.major,
393
- role: copy.roles.subtonic,
394
- state: 'scale-major',
395
- },
396
- {
397
- numeral: 'ii°',
398
- chord: this.dimChord(idx),
399
- chordType: copy.diminished,
400
- role: copy.roles.supertonic,
401
- state: 'scale-diminished',
402
- },
403
- ];
404
- }
473
+ const resolve = (ns) => ({ numerals: ns, chords: ns.map((n) => lookup.get(n) ?? n) });
474
+ // Chorus: rotate to lift chord (IV for major, VI for minor); else fixed fallback
475
+ const chorusPivot = type === 'major' ? 'IV' : 'VI';
476
+ const chorusIdx = numerals.indexOf(chorusPivot);
477
+ const chorus = chorusIdx > 0
478
+ ? resolve([...numerals.slice(chorusIdx), ...numerals.slice(0, chorusIdx)])
479
+ : resolve(type === 'major' ? ['I', 'IV', 'V', 'I'] : ['i', 'VI', 'III', 'VII']);
480
+ // Bridge: rotate to contrast chord (vi for major, III for minor); else fixed fallback
481
+ const bridgePivot = type === 'major' ? 'vi' : 'III';
482
+ const bridgeIdx = numerals.indexOf(bridgePivot);
483
+ const bridge = bridgeIdx >= 0
484
+ ? resolve([...numerals.slice(bridgeIdx), ...numerals.slice(0, bridgeIdx)])
485
+ : resolve(type === 'major' ? ['vi', 'IV', 'ii', 'V'] : ['III', 'VII', 'VI', 'iv']);
486
+ // Outro: fixed resolving pattern per mode
487
+ const outro = resolve(type === 'major' ? ['I', 'V', 'IV', 'I'] : ['i', 'VII', 'VI', 'i']);
488
+ return [
489
+ { label: copy.chorus, ...chorus },
490
+ { label: copy.bridge, ...bridge },
491
+ { label: copy.outro, ...outro },
492
+ ];
405
493
  }
406
494
  get progressions() {
407
495
  const table = this.chordTable;
@@ -409,21 +497,23 @@ class CircleOfFifthsComponent {
409
497
  return [];
410
498
  const lookup = new Map(table.map((r) => [r.numeral, r.chord]));
411
499
  const language = this.language();
412
- const defs = PROGRESSIONS[this.selectedType() === 'major' ? 'major' : 'minor'];
500
+ const type = this.selectedType() === 'major' ? 'major' : 'minor';
501
+ const defs = this.activeProgressionDefs() ?? PROGRESSIONS[type].slice(0, 4);
413
502
  return defs.map((definition) => ({
414
503
  name: definition.name[language],
415
504
  mood: definition.mood[language],
416
505
  genre: definition.genre[language],
417
506
  numerals: definition.numerals,
418
507
  chords: definition.numerals.map((numeral) => lookup.get(numeral) ?? numeral),
508
+ sections: this.deriveSections(definition.numerals, lookup),
419
509
  }));
420
510
  }
421
511
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CircleOfFifthsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
422
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: CircleOfFifthsComponent, isStandalone: true, selector: "the-chords-circle-of-fifths", inputs: { language: { classPropertyName: "language", publicName: "language", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.lang": "language()" } }, ngImport: i0, template: "<div class=\"page\">\n <header class=\"page-header\">\n <h1>{{ text().title }}</h1>\n <p>{{ text().subtitle }}</p>\n </header>\n\n <div class=\"circle-wrapper\">\n <svg\n viewBox=\"0 0 600 600\"\n xmlns=\"http://www.w3.org/2000/svg\"\n [attr.aria-label]=\"text().ariaLabel\"\n >\n <!-- \u2500\u2500 Key Signature Ring \u2500\u2500 -->\n @for (key of keys; track key.index) {\n <path [attr.d]=\"arcPath(KEY_SIG.inner, KEY_SIG.outer, key.index)\" class=\"keysig-segment\" />\n <text\n [attr.x]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).y\"\n class=\"keysig-label\"\n >\n {{ getAccidentalText(key) }}\n </text>\n }\n\n <!-- \u2500\u2500 Major Keys Ring \u2500\u2500 -->\n @for (key of keys; track key.index) {\n <g\n [ngClass]=\"['major-group', getMajorState(key.index)]\"\n (click)=\"selectKey(key.index, 'major')\"\n >\n <path [attr.d]=\"arcPath(MAJOR.inner, MAJOR.outer, key.index)\" />\n\n <text\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\n [attr.y]=\"\n textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y +\n (getMajorRole(key.index) ? -9 : 0)\n \"\n class=\"major-label\"\n >\n {{ key.major }}\n </text>\n\n @if (getMajorRole(key.index)) {\n <text\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y + 11\"\n class=\"role-label\"\n >\n {{ getMajorRole(key.index) }}\n </text>\n }\n </g>\n }\n\n <!-- \u2500\u2500 Minor Keys Ring \u2500\u2500 -->\n @for (key of keys; track key.index) {\n <g\n [ngClass]=\"['minor-group', getMinorState(key.index)]\"\n (click)=\"selectKey(key.index, 'minor')\"\n >\n <path [attr.d]=\"arcPath(MINOR.inner, MINOR.outer, key.index)\" />\n\n <text\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\n [attr.y]=\"\n textPos((MINOR.inner + MINOR.outer) / 2, key.index).y +\n (getMinorRole(key.index) ? -8 : 0)\n \"\n class=\"minor-label\"\n >\n {{ key.minor }}\n </text>\n\n @if (getMinorRole(key.index)) {\n <text\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).y + 9\"\n class=\"role-label\"\n >\n {{ getMinorRole(key.index) }}\n </text>\n }\n </g>\n }\n\n <!-- \u2500\u2500 Outer border \u2500\u2500 -->\n <circle cx=\"300\" cy=\"300\" r=\"289\" class=\"outer-border\" />\n\n <!-- \u2500\u2500 Center Circle \u2500\u2500 -->\n <circle cx=\"300\" cy=\"300\" r=\"99\" class=\"center-circle\" />\n\n @if (selectedInfo) {\n <text x=\"300\" y=\"283\" class=\"center-name\">{{ selectedInfo.name }}</text>\n <text x=\"300\" y=\"304\" class=\"center-scale\">{{ selectedInfo.scale }}</text>\n <text x=\"300\" y=\"322\" class=\"center-relative\">{{ selectedInfo.relativeKey }}</text>\n <text x=\"300\" y=\"338\" class=\"center-deselect\">{{ text().deselect }}</text>\n } @else {\n <text x=\"300\" y=\"293\" class=\"center-hint\">{{ text().clickKey }}</text>\n <text x=\"300\" y=\"312\" class=\"center-hint-sm\">{{ text().seeChords }}</text>\n }\n </svg>\n </div>\n\n <!-- \u2500\u2500 Legend \u2500\u2500 -->\n <div class=\"legend\">\n <span class=\"legend-item tonic\">{{ text().tonicLegend }}</span>\n <span class=\"legend-item scale-major\">{{ text().majorLegend }}</span>\n <span class=\"legend-item scale-minor\">{{ text().minorLegend }}</span>\n <span class=\"legend-item scale-diminished\">{{ text().diminishedLegend }}</span>\n </div>\n\n <!-- \u2500\u2500 Chord Table \u2500\u2500 -->\n @if (selectedInfo && chordTable.length) {\n <section class=\"chord-section\">\n <h2>{{ selectedInfo.fullName }} \u2014 {{ text().diatonicChords }}</h2>\n <p class=\"relative-label\">{{ selectedInfo.relativeKey }}</p>\n <div class=\"chord-grid\">\n @for (row of chordTable; track row.numeral) {\n <div class=\"chord-card\" [ngClass]=\"row.state\">\n <div class=\"chord-numeral\">{{ row.numeral }}</div>\n <div class=\"chord-name\">{{ row.chord }}</div>\n <div class=\"chord-type\">{{ row.chordType }}</div>\n <div class=\"chord-role\">{{ row.role }}</div>\n </div>\n }\n </div>\n </section>\n }\n\n <!-- \u2500\u2500 Chord Progressions \u2500\u2500 -->\n @if (progressions.length) {\n <section class=\"progressions-section\">\n <h2>{{ text().commonProgressions }} {{ selectedInfo?.fullName }}</h2>\n <div class=\"progression-list\">\n @for (prog of progressions; track prog.name) {\n <div class=\"progression-card\">\n <div class=\"prog-header\">\n <span class=\"prog-mood\">{{ prog.mood }}</span>\n <span class=\"prog-name\">{{ prog.name }}</span>\n <span class=\"prog-genre\">{{ prog.genre }}</span>\n </div>\n <div class=\"prog-chords\">\n @for (chord of prog.chords; track $index; let last = $last) {\n <span class=\"prog-chord\">{{ chord }}</span>\n @if (!last) {\n <span class=\"prog-arrow\">\u2192</span>\n }\n }\n </div>\n <div class=\"prog-numerals\">\n @for (num of prog.numerals; track $index; let last = $last) {\n <span>{{ num }}</span>\n @if (!last) {\n <span class=\"prog-arrow\">\u2192</span>\n }\n }\n </div>\n </div>\n }\n </div>\n </section>\n }\n</div>\n", styles: [":host{--_chords-background: var(--chords-background, #f0f4f8);--_chords-background-alt: var(--chords-background-alt, #f8fafc);--_chords-surface: var(--chords-surface, #ffffff);--_chords-surface-elevated: var(--chords-surface-elevated, #f8fafc);--_chords-text: var(--chords-text, #1e293b);--_chords-muted: var(--chords-muted, #64748b);--_chords-primary: var(--chords-primary, #3b82f6);--_chords-secondary: var(--chords-secondary, #06b6d4);--_chords-highlight: var(--chords-highlight, #f59e0b);--_chords-danger: var(--chords-danger, #a855f7);--_chords-border: var(--chords-border, #e2e8f0);--_chords-on-primary: var(--chords-on-primary, #ffffff);box-sizing:border-box;display:block;min-height:100%;background:var(--_chords-background);color:var(--_chords-text)}:host *,:host *:before,:host *:after{box-sizing:inherit}.page{max-width:720px;margin:0 auto;padding:24px 16px 48px;font-family:Segoe UI,system-ui,sans-serif}.page-header{text-align:center;margin-bottom:28px}.page-header h1{margin:0 0 6px;font-size:2rem;font-weight:800;color:var(--_chords-text);letter-spacing:-.02em}.page-header p{margin:0;color:var(--_chords-muted);font-size:.95rem}.circle-wrapper{max-width:580px;margin:0 auto}.circle-wrapper svg{display:block;width:100%;height:auto;overflow:visible}.keysig-segment{fill:var(--_chords-text)}.keysig-label{fill:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background));font-size:12px;font-weight:500;text-anchor:middle;dominant-baseline:central;pointer-events:none}.major-group{cursor:pointer}.major-group path{fill:color-mix(in srgb,var(--_chords-primary) 14%,var(--_chords-surface));stroke:var(--_chords-surface);stroke-width:2;transition:fill .18s ease}.major-group:hover path{fill:color-mix(in srgb,var(--_chords-primary) 24%,var(--_chords-surface))}.major-group .major-label{fill:var(--_chords-text);font-size:15px;font-weight:700;text-anchor:middle;dominant-baseline:central;pointer-events:none;transition:fill .18s ease}.major-group.tonic path{fill:var(--_chords-highlight)}.major-group.tonic:hover path{fill:color-mix(in srgb,var(--_chords-highlight) 84%,var(--_chords-text))}.major-group.tonic .major-label{fill:var(--_chords-on-primary)}.major-group.scale-major path{fill:var(--_chords-primary)}.major-group.scale-major:hover path{fill:color-mix(in srgb,var(--_chords-primary) 82%,var(--_chords-text))}.major-group.scale-major .major-label{fill:var(--_chords-on-primary)}.minor-group{cursor:pointer}.minor-group path{fill:color-mix(in srgb,var(--_chords-secondary) 10%,var(--_chords-surface));stroke:var(--_chords-surface);stroke-width:2;transition:fill .18s ease}.minor-group:hover path{fill:color-mix(in srgb,var(--_chords-secondary) 20%,var(--_chords-surface))}.minor-group .minor-label{fill:color-mix(in srgb,var(--_chords-text) 78%,var(--_chords-muted));font-size:12px;font-weight:600;text-anchor:middle;dominant-baseline:central;pointer-events:none;transition:fill .18s ease}.minor-group.tonic path{fill:var(--_chords-highlight)}.minor-group.tonic:hover path{fill:color-mix(in srgb,var(--_chords-highlight) 84%,var(--_chords-text))}.minor-group.tonic .minor-label{fill:var(--_chords-on-primary)}.minor-group.scale-minor path{fill:var(--_chords-secondary)}.minor-group.scale-minor:hover path{fill:color-mix(in srgb,var(--_chords-secondary) 82%,var(--_chords-text))}.minor-group.scale-minor .minor-label{fill:var(--_chords-on-primary)}.minor-group.scale-diminished path{fill:var(--_chords-danger)}.minor-group.scale-diminished:hover path{fill:color-mix(in srgb,var(--_chords-danger) 82%,var(--_chords-text))}.minor-group.scale-diminished .minor-label{fill:var(--_chords-on-primary)}.role-label{font-size:10px;font-style:italic;font-weight:600;text-anchor:middle;dominant-baseline:central;pointer-events:none;fill:color-mix(in srgb,var(--_chords-on-primary) 85%,transparent)}.outer-border{fill:none;stroke:var(--_chords-text);stroke-width:2.5}.center-circle{fill:var(--_chords-surface);stroke:var(--_chords-border);stroke-width:2;filter:drop-shadow(0 2px 10px color-mix(in srgb,var(--_chords-text) 8%,transparent))}.center-name{font-size:30px;font-weight:800;text-anchor:middle;dominant-baseline:auto;fill:var(--_chords-text);letter-spacing:-.02em}.center-scale{font-size:13px;font-weight:600;text-anchor:middle;dominant-baseline:auto;fill:var(--_chords-muted);text-transform:uppercase;letter-spacing:.08em}.center-relative{font-size:9.5px;font-weight:500;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.center-deselect{font-size:8px;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 42%,var(--_chords-background))}.center-hint{font-size:16px;font-weight:600;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.center-hint-sm{font-size:11px;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 42%,var(--_chords-background))}.legend{display:flex;justify-content:center;gap:16px;flex-wrap:wrap;margin:16px 0 0}.legend .legend-item{display:flex;align-items:center;gap:6px;font-size:.8rem;color:var(--_chords-muted)}.legend .legend-item:before{content:\"\";display:inline-block;width:14px;height:14px;border-radius:3px}.legend .legend-item.tonic:before{background:var(--_chords-highlight)}.legend .legend-item.scale-major:before{background:var(--_chords-primary)}.legend .legend-item.scale-minor:before{background:var(--_chords-secondary)}.legend .legend-item.scale-diminished:before{background:var(--_chords-danger)}.chord-section{margin-top:36px;text-align:center}.chord-section h2{margin:0 0 4px;font-size:1.25rem;font-weight:700;color:var(--_chords-text)}.chord-section .relative-label{margin:0 0 20px;font-size:.88rem;color:var(--_chords-muted)}.chord-grid{display:flex;flex-wrap:nowrap;gap:8px;justify-content:center;overflow-x:auto;padding-bottom:4px}.chord-card{border-radius:12px;padding:12px 14px;min-width:82px;flex-shrink:0;text-align:center;background:color-mix(in srgb,var(--_chords-text) 8%,var(--_chords-surface));border:2px solid transparent;transition:transform .15s ease,box-shadow .15s ease}.chord-card:hover{transform:translateY(-2px);box-shadow:0 4px 16px color-mix(in srgb,var(--_chords-text) 10%,transparent)}.chord-card .chord-numeral{font-size:.85rem;font-weight:700;font-style:italic;color:var(--_chords-muted)}.chord-card .chord-name{font-size:1.4rem;font-weight:800;color:var(--_chords-text);margin:2px 0;letter-spacing:-.02em}.chord-card .chord-type{font-size:.65rem;text-transform:uppercase;letter-spacing:.06em;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.chord-card .chord-role{font-size:.7rem;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background));margin-top:2px}.chord-card.tonic{background:color-mix(in srgb,var(--_chords-highlight) 10%,var(--_chords-surface));border-color:var(--_chords-highlight)}.chord-card.tonic .chord-numeral,.chord-card.tonic .chord-name{color:color-mix(in srgb,var(--_chords-highlight) 58%,var(--_chords-text))}.chord-card.scale-major{background:color-mix(in srgb,var(--_chords-primary) 9%,var(--_chords-surface));border-color:var(--_chords-primary)}.chord-card.scale-major .chord-numeral,.chord-card.scale-major .chord-name{color:color-mix(in srgb,var(--_chords-primary) 65%,var(--_chords-text))}.chord-card.scale-diminished{background:color-mix(in srgb,var(--_chords-danger) 9%,var(--_chords-surface));border-color:var(--_chords-danger)}.chord-card.scale-diminished .chord-numeral,.chord-card.scale-diminished .chord-name{color:color-mix(in srgb,var(--_chords-danger) 65%,var(--_chords-text))}.progressions-section{margin-top:36px;text-align:center}.progressions-section h2{margin:0 0 20px;font-size:1.25rem;font-weight:700;color:var(--_chords-text)}.progression-list{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}@media(max-width:480px){.progression-list{grid-template-columns:1fr}}.progression-card{background:var(--_chords-surface);border:1.5px solid var(--_chords-border);border-radius:14px;padding:16px 18px;text-align:left;transition:box-shadow .15s ease,transform .15s ease}.progression-card:hover{box-shadow:0 4px 16px color-mix(in srgb,var(--_chords-text) 8%,transparent);transform:translateY(-2px)}.prog-header{display:flex;align-items:baseline;gap:8px;margin-bottom:12px;flex-wrap:wrap}.prog-mood{font-size:.95rem;font-weight:700;color:var(--_chords-text)}.prog-name{font-size:.75rem;font-weight:600;color:var(--_chords-muted);text-transform:uppercase;letter-spacing:.06em}.prog-genre{margin-left:auto;font-size:.7rem;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background));font-style:italic}.prog-chords{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:6px}.prog-chord{font-size:1.15rem;font-weight:800;color:var(--_chords-text);letter-spacing:-.02em}.prog-numerals{display:flex;align-items:center;gap:6px;font-size:.75rem;font-style:italic;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.prog-arrow{color:color-mix(in srgb,var(--_chords-muted) 42%,var(--_chords-background));font-size:.8rem}@media(max-width:480px){.page-header h1{font-size:1.5rem}.chord-card{min-width:75px;padding:10px 12px}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
512
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: CircleOfFifthsComponent, isStandalone: true, selector: "the-chords-circle-of-fifths", inputs: { language: { classPropertyName: "language", publicName: "language", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.lang": "language()" } }, ngImport: i0, template: "<div class=\"page\">\n <header class=\"page-header\">\n <h1>{{ text().title }}</h1>\n <p>{{ text().subtitle }}</p>\n </header>\n\n <div class=\"circle-wrapper\">\n <svg\n viewBox=\"0 0 600 600\"\n xmlns=\"http://www.w3.org/2000/svg\"\n [attr.aria-label]=\"text().ariaLabel\"\n >\n <!-- \u2500\u2500 Key Signature Ring \u2500\u2500 -->\n @for (key of keys; track key.index) {\n <path [attr.d]=\"arcPath(KEY_SIG.inner, KEY_SIG.outer, key.index)\" class=\"keysig-segment\" />\n <text\n [attr.x]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).y\"\n class=\"keysig-label\"\n >\n {{ getAccidentalText(key) }}\n </text>\n }\n\n <!-- \u2500\u2500 Major Keys Ring \u2500\u2500 -->\n @for (key of keys; track key.index) {\n <g\n [ngClass]=\"['major-group', getMajorState(key.index)]\"\n (click)=\"selectKey(key.index, 'major')\"\n >\n <path [attr.d]=\"arcPath(MAJOR.inner, MAJOR.outer, key.index)\" />\n\n <text\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\n [attr.y]=\"\n textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y +\n (getMajorRole(key.index) ? -9 : 0)\n \"\n class=\"major-label\"\n >\n {{ key.major }}\n </text>\n\n @if (getMajorRole(key.index)) {\n <text\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y + 11\"\n class=\"role-label\"\n >\n {{ getMajorRole(key.index) }}\n </text>\n }\n </g>\n }\n\n <!-- \u2500\u2500 Minor Keys Ring \u2500\u2500 -->\n @for (key of keys; track key.index) {\n <g\n [ngClass]=\"['minor-group', getMinorState(key.index)]\"\n (click)=\"selectKey(key.index, 'minor')\"\n >\n <path [attr.d]=\"arcPath(MINOR.inner, MINOR.outer, key.index)\" />\n\n <text\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\n [attr.y]=\"\n textPos((MINOR.inner + MINOR.outer) / 2, key.index).y +\n (getMinorRole(key.index) ? -8 : 0)\n \"\n class=\"minor-label\"\n >\n {{ key.minor }}\n </text>\n\n @if (getMinorRole(key.index)) {\n <text\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).y + 9\"\n class=\"role-label\"\n >\n {{ getMinorRole(key.index) }}\n </text>\n }\n </g>\n }\n\n <!-- \u2500\u2500 Outer border \u2500\u2500 -->\n <circle cx=\"300\" cy=\"300\" r=\"289\" class=\"outer-border\" />\n\n <!-- \u2500\u2500 Center Circle \u2500\u2500 -->\n <circle cx=\"300\" cy=\"300\" r=\"99\" class=\"center-circle\" />\n\n @if (selectedInfo) {\n <text x=\"300\" y=\"283\" class=\"center-name\">{{ selectedInfo.name }}</text>\n <text x=\"300\" y=\"304\" class=\"center-scale\">{{ selectedInfo.scale }}</text>\n <text x=\"300\" y=\"322\" class=\"center-relative\">{{ selectedInfo.relativeKey }}</text>\n <text x=\"300\" y=\"338\" class=\"center-deselect\">{{ text().deselect }}</text>\n } @else {\n <text x=\"300\" y=\"293\" class=\"center-hint\">{{ text().clickKey }}</text>\n <text x=\"300\" y=\"312\" class=\"center-hint-sm\">{{ text().seeChords }}</text>\n }\n </svg>\n </div>\n\n <!-- \u2500\u2500 Legend \u2500\u2500 -->\n <div class=\"legend\">\n <span class=\"legend-item tonic\">{{ text().tonicLegend }}</span>\n <span class=\"legend-item scale-major\">{{ text().majorLegend }}</span>\n <span class=\"legend-item scale-minor\">{{ text().minorLegend }}</span>\n <span class=\"legend-item scale-diminished\">{{ text().diminishedLegend }}</span>\n </div>\n\n <chord-section [chords]=\"chordNames\" [language]=\"language()\"></chord-section>\n\n <!-- \u2500\u2500 Chord Progressions \u2500\u2500 -->\n @if (progressions.length) {\n <section class=\"progressions-section\">\n <div class=\"progressions-header\">\n <h2>{{ text().commonProgressions }} {{ selectedInfo?.fullName }}</h2>\n <button class=\"btn-randomize\" (click)=\"randomizeProgressions()\">{{ text().generateProgressions }}</button>\n </div>\n <div class=\"progression-list\">\n @for (prog of progressions; track prog.name) {\n <div class=\"progression-card\">\n <div class=\"prog-header\">\n <span class=\"prog-mood\">{{ prog.mood }}</span>\n <span class=\"prog-name\">{{ prog.name }}</span>\n <span class=\"prog-genre\">{{ prog.genre }}</span>\n </div>\n <div class=\"prog-chords\">\n @for (chord of prog.chords; track $index; let last = $last) {\n <span class=\"prog-chord\">{{ chord }}</span>\n @if (!last) {\n <span class=\"prog-arrow\">\u2192</span>\n }\n }\n </div>\n <div class=\"prog-numerals\">\n @for (num of prog.numerals; track $index; let last = $last) {\n <span>{{ num }}</span>\n @if (!last) {\n <span class=\"prog-arrow\">\u2192</span>\n }\n }\n </div>\n <div class=\"prog-expand-row\">\n <button class=\"btn-expand\" (click)=\"toggleCard(prog.name)\">\n {{ expandedCards().has(prog.name) ? text().collapse : text().expand }}\n </button>\n <button class=\"btn-expand\" (click)=\"copyChords(prog)\">\n {{ text().copy }}\n </button>\n </div>\n @if (expandedCards().has(prog.name)) {\n <div class=\"prog-sections\">\n @for (section of prog.sections; track section.label) {\n <div class=\"prog-section\">\n <div class=\"prog-section-label\">{{ section.label }}</div>\n <div class=\"prog-chords\">\n @for (chord of section.chords; track $index; let last = $last) {\n <span class=\"prog-chord\">{{ chord }}</span>\n @if (!last) { <span class=\"prog-arrow\">\u2192</span> }\n }\n </div>\n <div class=\"prog-numerals\">\n @for (num of section.numerals; track $index; let last = $last) {\n <span>{{ num }}</span>\n @if (!last) { <span class=\"prog-arrow\">\u2192</span> }\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </section>\n }\n</div>\n", styles: [":host{--_chords-background: var(--chords-background, #f0f4f8);--_chords-background-alt: var(--chords-background-alt, #f8fafc);--_chords-surface: var(--chords-surface, #ffffff);--_chords-surface-elevated: var(--chords-surface-elevated, #f8fafc);--_chords-text: var(--chords-text, #1e293b);--_chords-muted: var(--chords-muted, #64748b);--_chords-primary: var(--chords-primary, #3b82f6);--_chords-secondary: var(--chords-secondary, #06b6d4);--_chords-highlight: var(--chords-highlight, #f59e0b);--_chords-danger: var(--chords-danger, #a855f7);--_chords-border: var(--chords-border, #e2e8f0);--_chords-on-primary: var(--chords-on-primary, #ffffff);box-sizing:border-box;display:block;min-height:100%;background:var(--_chords-background);color:var(--_chords-text)}:host *,:host *:before,:host *:after{box-sizing:inherit}.page{max-width:720px;margin:0 auto;padding:24px 16px 48px;font-family:Segoe UI,system-ui,sans-serif}.page-header{text-align:center;margin-bottom:28px}.page-header h1{margin:0 0 6px;font-size:2rem;font-weight:800;color:var(--_chords-text);letter-spacing:-.02em}.page-header p{margin:0;color:var(--_chords-muted);font-size:.95rem}.circle-wrapper{max-width:580px;margin:0 auto}.circle-wrapper svg{display:block;width:100%;height:auto;overflow:visible}.keysig-segment{fill:var(--_chords-text)}.keysig-label{fill:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background));font-size:12px;font-weight:500;text-anchor:middle;dominant-baseline:central;pointer-events:none}.major-group{cursor:pointer}.major-group path{fill:color-mix(in srgb,var(--_chords-primary) 14%,var(--_chords-surface));stroke:var(--_chords-surface);stroke-width:2;transition:fill .18s ease}.major-group:hover path{fill:color-mix(in srgb,var(--_chords-primary) 24%,var(--_chords-surface))}.major-group .major-label{fill:var(--_chords-text);font-size:15px;font-weight:700;text-anchor:middle;dominant-baseline:central;pointer-events:none;transition:fill .18s ease}.major-group.tonic path{fill:var(--_chords-highlight)}.major-group.tonic:hover path{fill:color-mix(in srgb,var(--_chords-highlight) 84%,var(--_chords-text))}.major-group.tonic .major-label{fill:var(--_chords-on-primary)}.major-group.scale-major path{fill:var(--_chords-primary)}.major-group.scale-major:hover path{fill:color-mix(in srgb,var(--_chords-primary) 82%,var(--_chords-text))}.major-group.scale-major .major-label{fill:var(--_chords-on-primary)}.minor-group{cursor:pointer}.minor-group path{fill:color-mix(in srgb,var(--_chords-secondary) 10%,var(--_chords-surface));stroke:var(--_chords-surface);stroke-width:2;transition:fill .18s ease}.minor-group:hover path{fill:color-mix(in srgb,var(--_chords-secondary) 20%,var(--_chords-surface))}.minor-group .minor-label{fill:color-mix(in srgb,var(--_chords-text) 78%,var(--_chords-muted));font-size:12px;font-weight:600;text-anchor:middle;dominant-baseline:central;pointer-events:none;transition:fill .18s ease}.minor-group.tonic path{fill:var(--_chords-highlight)}.minor-group.tonic:hover path{fill:color-mix(in srgb,var(--_chords-highlight) 84%,var(--_chords-text))}.minor-group.tonic .minor-label{fill:var(--_chords-on-primary)}.minor-group.scale-minor path{fill:var(--_chords-secondary)}.minor-group.scale-minor:hover path{fill:color-mix(in srgb,var(--_chords-secondary) 82%,var(--_chords-text))}.minor-group.scale-minor .minor-label{fill:var(--_chords-on-primary)}.minor-group.scale-diminished path{fill:var(--_chords-danger)}.minor-group.scale-diminished:hover path{fill:color-mix(in srgb,var(--_chords-danger) 82%,var(--_chords-text))}.minor-group.scale-diminished .minor-label{fill:var(--_chords-on-primary)}.role-label{font-size:10px;font-style:italic;font-weight:600;text-anchor:middle;dominant-baseline:central;pointer-events:none;fill:color-mix(in srgb,var(--_chords-on-primary) 85%,transparent)}.outer-border{fill:none;stroke:var(--_chords-text);stroke-width:2.5}.center-circle{fill:var(--_chords-surface);stroke:var(--_chords-border);stroke-width:2;filter:drop-shadow(0 2px 10px color-mix(in srgb,var(--_chords-text) 8%,transparent))}.center-name{font-size:30px;font-weight:800;text-anchor:middle;dominant-baseline:auto;fill:var(--_chords-text);letter-spacing:-.02em}.center-scale{font-size:13px;font-weight:600;text-anchor:middle;dominant-baseline:auto;fill:var(--_chords-muted);text-transform:uppercase;letter-spacing:.08em}.center-relative{font-size:9.5px;font-weight:500;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.center-deselect{font-size:8px;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 42%,var(--_chords-background))}.center-hint{font-size:16px;font-weight:600;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.center-hint-sm{font-size:11px;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 42%,var(--_chords-background))}.legend{display:flex;justify-content:center;gap:16px;flex-wrap:wrap;margin:16px 0 0}.legend .legend-item{display:flex;align-items:center;gap:6px;font-size:.8rem;color:var(--_chords-muted)}.legend .legend-item:before{content:\"\";display:inline-block;width:14px;height:14px;border-radius:3px}.legend .legend-item.tonic:before{background:var(--_chords-highlight)}.legend .legend-item.scale-major:before{background:var(--_chords-primary)}.legend .legend-item.scale-minor:before{background:var(--_chords-secondary)}.legend .legend-item.scale-diminished:before{background:var(--_chords-danger)}.progressions-section{margin-top:36px;text-align:center}.progressions-header{display:flex;align-items:center;justify-content:center;gap:12px;margin-bottom:20px;flex-wrap:wrap}.progressions-header h2{margin:0;font-size:1.25rem;font-weight:700;color:var(--_chords-text)}.btn-randomize{padding:6px 14px;font-size:.8rem;font-weight:600;border:1.5px solid var(--_chords-primary);border-radius:20px;background:transparent;color:var(--_chords-primary);cursor:pointer;transition:background .15s,color .15s;white-space:nowrap}.btn-randomize:hover{background:var(--_chords-primary);color:var(--_chords-on-primary)}.progression-list{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}@media(max-width:480px){.progression-list{grid-template-columns:1fr}}.progression-card{background:var(--_chords-surface);border:1.5px solid var(--_chords-border);border-radius:14px;padding:16px 18px;text-align:left;transition:box-shadow .15s ease,transform .15s ease}.progression-card:hover{box-shadow:0 4px 16px color-mix(in srgb,var(--_chords-text) 8%,transparent);transform:translateY(-2px)}.prog-header{display:flex;align-items:baseline;gap:8px;margin-bottom:12px;flex-wrap:wrap}.prog-mood{font-size:.95rem;font-weight:700;color:var(--_chords-text)}.prog-name{font-size:.75rem;font-weight:600;color:var(--_chords-muted);text-transform:uppercase;letter-spacing:.06em}.prog-genre{margin-left:auto;font-size:.7rem;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background));font-style:italic}.prog-chords{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:6px}.prog-chord{font-size:1.15rem;font-weight:800;color:var(--_chords-text);letter-spacing:-.02em}.prog-numerals{display:flex;align-items:center;gap:6px;font-size:.75rem;font-style:italic;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.prog-expand-row{display:flex;justify-content:flex-end;margin-top:10px}.btn-expand{padding:3px 10px;font-size:.72rem;font-weight:600;border:1px solid var(--_chords-border);border-radius:12px;background:transparent;color:var(--_chords-muted);cursor:pointer;transition:border-color .15s,color .15s}.btn-expand:hover{border-color:var(--_chords-primary);color:var(--_chords-primary)}.prog-sections{margin-top:12px;border-top:1px solid var(--_chords-border);padding-top:10px;display:flex;flex-direction:column;gap:10px}.prog-section{padding:8px 10px;border-radius:8px;background:color-mix(in srgb,var(--_chords-text) 4%,var(--_chords-surface))}.prog-section-label{font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--_chords-muted);margin-bottom:5px}.prog-arrow{color:color-mix(in srgb,var(--_chords-muted) 42%,var(--_chords-background));font-size:.8rem}@media(max-width:480px){.page-header h1{font-size:1.5rem}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ChordSectionComponent, selector: "chord-section", inputs: ["chords", "language"] }] });
423
513
  }
424
514
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CircleOfFifthsComponent, decorators: [{
425
515
  type: Component,
426
- args: [{ selector: 'the-chords-circle-of-fifths', standalone: true, imports: [NgClass], host: { '[attr.lang]': 'language()' }, template: "<div class=\"page\">\n <header class=\"page-header\">\n <h1>{{ text().title }}</h1>\n <p>{{ text().subtitle }}</p>\n </header>\n\n <div class=\"circle-wrapper\">\n <svg\n viewBox=\"0 0 600 600\"\n xmlns=\"http://www.w3.org/2000/svg\"\n [attr.aria-label]=\"text().ariaLabel\"\n >\n <!-- \u2500\u2500 Key Signature Ring \u2500\u2500 -->\n @for (key of keys; track key.index) {\n <path [attr.d]=\"arcPath(KEY_SIG.inner, KEY_SIG.outer, key.index)\" class=\"keysig-segment\" />\n <text\n [attr.x]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).y\"\n class=\"keysig-label\"\n >\n {{ getAccidentalText(key) }}\n </text>\n }\n\n <!-- \u2500\u2500 Major Keys Ring \u2500\u2500 -->\n @for (key of keys; track key.index) {\n <g\n [ngClass]=\"['major-group', getMajorState(key.index)]\"\n (click)=\"selectKey(key.index, 'major')\"\n >\n <path [attr.d]=\"arcPath(MAJOR.inner, MAJOR.outer, key.index)\" />\n\n <text\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\n [attr.y]=\"\n textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y +\n (getMajorRole(key.index) ? -9 : 0)\n \"\n class=\"major-label\"\n >\n {{ key.major }}\n </text>\n\n @if (getMajorRole(key.index)) {\n <text\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y + 11\"\n class=\"role-label\"\n >\n {{ getMajorRole(key.index) }}\n </text>\n }\n </g>\n }\n\n <!-- \u2500\u2500 Minor Keys Ring \u2500\u2500 -->\n @for (key of keys; track key.index) {\n <g\n [ngClass]=\"['minor-group', getMinorState(key.index)]\"\n (click)=\"selectKey(key.index, 'minor')\"\n >\n <path [attr.d]=\"arcPath(MINOR.inner, MINOR.outer, key.index)\" />\n\n <text\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\n [attr.y]=\"\n textPos((MINOR.inner + MINOR.outer) / 2, key.index).y +\n (getMinorRole(key.index) ? -8 : 0)\n \"\n class=\"minor-label\"\n >\n {{ key.minor }}\n </text>\n\n @if (getMinorRole(key.index)) {\n <text\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).y + 9\"\n class=\"role-label\"\n >\n {{ getMinorRole(key.index) }}\n </text>\n }\n </g>\n }\n\n <!-- \u2500\u2500 Outer border \u2500\u2500 -->\n <circle cx=\"300\" cy=\"300\" r=\"289\" class=\"outer-border\" />\n\n <!-- \u2500\u2500 Center Circle \u2500\u2500 -->\n <circle cx=\"300\" cy=\"300\" r=\"99\" class=\"center-circle\" />\n\n @if (selectedInfo) {\n <text x=\"300\" y=\"283\" class=\"center-name\">{{ selectedInfo.name }}</text>\n <text x=\"300\" y=\"304\" class=\"center-scale\">{{ selectedInfo.scale }}</text>\n <text x=\"300\" y=\"322\" class=\"center-relative\">{{ selectedInfo.relativeKey }}</text>\n <text x=\"300\" y=\"338\" class=\"center-deselect\">{{ text().deselect }}</text>\n } @else {\n <text x=\"300\" y=\"293\" class=\"center-hint\">{{ text().clickKey }}</text>\n <text x=\"300\" y=\"312\" class=\"center-hint-sm\">{{ text().seeChords }}</text>\n }\n </svg>\n </div>\n\n <!-- \u2500\u2500 Legend \u2500\u2500 -->\n <div class=\"legend\">\n <span class=\"legend-item tonic\">{{ text().tonicLegend }}</span>\n <span class=\"legend-item scale-major\">{{ text().majorLegend }}</span>\n <span class=\"legend-item scale-minor\">{{ text().minorLegend }}</span>\n <span class=\"legend-item scale-diminished\">{{ text().diminishedLegend }}</span>\n </div>\n\n <!-- \u2500\u2500 Chord Table \u2500\u2500 -->\n @if (selectedInfo && chordTable.length) {\n <section class=\"chord-section\">\n <h2>{{ selectedInfo.fullName }} \u2014 {{ text().diatonicChords }}</h2>\n <p class=\"relative-label\">{{ selectedInfo.relativeKey }}</p>\n <div class=\"chord-grid\">\n @for (row of chordTable; track row.numeral) {\n <div class=\"chord-card\" [ngClass]=\"row.state\">\n <div class=\"chord-numeral\">{{ row.numeral }}</div>\n <div class=\"chord-name\">{{ row.chord }}</div>\n <div class=\"chord-type\">{{ row.chordType }}</div>\n <div class=\"chord-role\">{{ row.role }}</div>\n </div>\n }\n </div>\n </section>\n }\n\n <!-- \u2500\u2500 Chord Progressions \u2500\u2500 -->\n @if (progressions.length) {\n <section class=\"progressions-section\">\n <h2>{{ text().commonProgressions }} {{ selectedInfo?.fullName }}</h2>\n <div class=\"progression-list\">\n @for (prog of progressions; track prog.name) {\n <div class=\"progression-card\">\n <div class=\"prog-header\">\n <span class=\"prog-mood\">{{ prog.mood }}</span>\n <span class=\"prog-name\">{{ prog.name }}</span>\n <span class=\"prog-genre\">{{ prog.genre }}</span>\n </div>\n <div class=\"prog-chords\">\n @for (chord of prog.chords; track $index; let last = $last) {\n <span class=\"prog-chord\">{{ chord }}</span>\n @if (!last) {\n <span class=\"prog-arrow\">\u2192</span>\n }\n }\n </div>\n <div class=\"prog-numerals\">\n @for (num of prog.numerals; track $index; let last = $last) {\n <span>{{ num }}</span>\n @if (!last) {\n <span class=\"prog-arrow\">\u2192</span>\n }\n }\n </div>\n </div>\n }\n </div>\n </section>\n }\n</div>\n", styles: [":host{--_chords-background: var(--chords-background, #f0f4f8);--_chords-background-alt: var(--chords-background-alt, #f8fafc);--_chords-surface: var(--chords-surface, #ffffff);--_chords-surface-elevated: var(--chords-surface-elevated, #f8fafc);--_chords-text: var(--chords-text, #1e293b);--_chords-muted: var(--chords-muted, #64748b);--_chords-primary: var(--chords-primary, #3b82f6);--_chords-secondary: var(--chords-secondary, #06b6d4);--_chords-highlight: var(--chords-highlight, #f59e0b);--_chords-danger: var(--chords-danger, #a855f7);--_chords-border: var(--chords-border, #e2e8f0);--_chords-on-primary: var(--chords-on-primary, #ffffff);box-sizing:border-box;display:block;min-height:100%;background:var(--_chords-background);color:var(--_chords-text)}:host *,:host *:before,:host *:after{box-sizing:inherit}.page{max-width:720px;margin:0 auto;padding:24px 16px 48px;font-family:Segoe UI,system-ui,sans-serif}.page-header{text-align:center;margin-bottom:28px}.page-header h1{margin:0 0 6px;font-size:2rem;font-weight:800;color:var(--_chords-text);letter-spacing:-.02em}.page-header p{margin:0;color:var(--_chords-muted);font-size:.95rem}.circle-wrapper{max-width:580px;margin:0 auto}.circle-wrapper svg{display:block;width:100%;height:auto;overflow:visible}.keysig-segment{fill:var(--_chords-text)}.keysig-label{fill:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background));font-size:12px;font-weight:500;text-anchor:middle;dominant-baseline:central;pointer-events:none}.major-group{cursor:pointer}.major-group path{fill:color-mix(in srgb,var(--_chords-primary) 14%,var(--_chords-surface));stroke:var(--_chords-surface);stroke-width:2;transition:fill .18s ease}.major-group:hover path{fill:color-mix(in srgb,var(--_chords-primary) 24%,var(--_chords-surface))}.major-group .major-label{fill:var(--_chords-text);font-size:15px;font-weight:700;text-anchor:middle;dominant-baseline:central;pointer-events:none;transition:fill .18s ease}.major-group.tonic path{fill:var(--_chords-highlight)}.major-group.tonic:hover path{fill:color-mix(in srgb,var(--_chords-highlight) 84%,var(--_chords-text))}.major-group.tonic .major-label{fill:var(--_chords-on-primary)}.major-group.scale-major path{fill:var(--_chords-primary)}.major-group.scale-major:hover path{fill:color-mix(in srgb,var(--_chords-primary) 82%,var(--_chords-text))}.major-group.scale-major .major-label{fill:var(--_chords-on-primary)}.minor-group{cursor:pointer}.minor-group path{fill:color-mix(in srgb,var(--_chords-secondary) 10%,var(--_chords-surface));stroke:var(--_chords-surface);stroke-width:2;transition:fill .18s ease}.minor-group:hover path{fill:color-mix(in srgb,var(--_chords-secondary) 20%,var(--_chords-surface))}.minor-group .minor-label{fill:color-mix(in srgb,var(--_chords-text) 78%,var(--_chords-muted));font-size:12px;font-weight:600;text-anchor:middle;dominant-baseline:central;pointer-events:none;transition:fill .18s ease}.minor-group.tonic path{fill:var(--_chords-highlight)}.minor-group.tonic:hover path{fill:color-mix(in srgb,var(--_chords-highlight) 84%,var(--_chords-text))}.minor-group.tonic .minor-label{fill:var(--_chords-on-primary)}.minor-group.scale-minor path{fill:var(--_chords-secondary)}.minor-group.scale-minor:hover path{fill:color-mix(in srgb,var(--_chords-secondary) 82%,var(--_chords-text))}.minor-group.scale-minor .minor-label{fill:var(--_chords-on-primary)}.minor-group.scale-diminished path{fill:var(--_chords-danger)}.minor-group.scale-diminished:hover path{fill:color-mix(in srgb,var(--_chords-danger) 82%,var(--_chords-text))}.minor-group.scale-diminished .minor-label{fill:var(--_chords-on-primary)}.role-label{font-size:10px;font-style:italic;font-weight:600;text-anchor:middle;dominant-baseline:central;pointer-events:none;fill:color-mix(in srgb,var(--_chords-on-primary) 85%,transparent)}.outer-border{fill:none;stroke:var(--_chords-text);stroke-width:2.5}.center-circle{fill:var(--_chords-surface);stroke:var(--_chords-border);stroke-width:2;filter:drop-shadow(0 2px 10px color-mix(in srgb,var(--_chords-text) 8%,transparent))}.center-name{font-size:30px;font-weight:800;text-anchor:middle;dominant-baseline:auto;fill:var(--_chords-text);letter-spacing:-.02em}.center-scale{font-size:13px;font-weight:600;text-anchor:middle;dominant-baseline:auto;fill:var(--_chords-muted);text-transform:uppercase;letter-spacing:.08em}.center-relative{font-size:9.5px;font-weight:500;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.center-deselect{font-size:8px;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 42%,var(--_chords-background))}.center-hint{font-size:16px;font-weight:600;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.center-hint-sm{font-size:11px;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 42%,var(--_chords-background))}.legend{display:flex;justify-content:center;gap:16px;flex-wrap:wrap;margin:16px 0 0}.legend .legend-item{display:flex;align-items:center;gap:6px;font-size:.8rem;color:var(--_chords-muted)}.legend .legend-item:before{content:\"\";display:inline-block;width:14px;height:14px;border-radius:3px}.legend .legend-item.tonic:before{background:var(--_chords-highlight)}.legend .legend-item.scale-major:before{background:var(--_chords-primary)}.legend .legend-item.scale-minor:before{background:var(--_chords-secondary)}.legend .legend-item.scale-diminished:before{background:var(--_chords-danger)}.chord-section{margin-top:36px;text-align:center}.chord-section h2{margin:0 0 4px;font-size:1.25rem;font-weight:700;color:var(--_chords-text)}.chord-section .relative-label{margin:0 0 20px;font-size:.88rem;color:var(--_chords-muted)}.chord-grid{display:flex;flex-wrap:nowrap;gap:8px;justify-content:center;overflow-x:auto;padding-bottom:4px}.chord-card{border-radius:12px;padding:12px 14px;min-width:82px;flex-shrink:0;text-align:center;background:color-mix(in srgb,var(--_chords-text) 8%,var(--_chords-surface));border:2px solid transparent;transition:transform .15s ease,box-shadow .15s ease}.chord-card:hover{transform:translateY(-2px);box-shadow:0 4px 16px color-mix(in srgb,var(--_chords-text) 10%,transparent)}.chord-card .chord-numeral{font-size:.85rem;font-weight:700;font-style:italic;color:var(--_chords-muted)}.chord-card .chord-name{font-size:1.4rem;font-weight:800;color:var(--_chords-text);margin:2px 0;letter-spacing:-.02em}.chord-card .chord-type{font-size:.65rem;text-transform:uppercase;letter-spacing:.06em;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.chord-card .chord-role{font-size:.7rem;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background));margin-top:2px}.chord-card.tonic{background:color-mix(in srgb,var(--_chords-highlight) 10%,var(--_chords-surface));border-color:var(--_chords-highlight)}.chord-card.tonic .chord-numeral,.chord-card.tonic .chord-name{color:color-mix(in srgb,var(--_chords-highlight) 58%,var(--_chords-text))}.chord-card.scale-major{background:color-mix(in srgb,var(--_chords-primary) 9%,var(--_chords-surface));border-color:var(--_chords-primary)}.chord-card.scale-major .chord-numeral,.chord-card.scale-major .chord-name{color:color-mix(in srgb,var(--_chords-primary) 65%,var(--_chords-text))}.chord-card.scale-diminished{background:color-mix(in srgb,var(--_chords-danger) 9%,var(--_chords-surface));border-color:var(--_chords-danger)}.chord-card.scale-diminished .chord-numeral,.chord-card.scale-diminished .chord-name{color:color-mix(in srgb,var(--_chords-danger) 65%,var(--_chords-text))}.progressions-section{margin-top:36px;text-align:center}.progressions-section h2{margin:0 0 20px;font-size:1.25rem;font-weight:700;color:var(--_chords-text)}.progression-list{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}@media(max-width:480px){.progression-list{grid-template-columns:1fr}}.progression-card{background:var(--_chords-surface);border:1.5px solid var(--_chords-border);border-radius:14px;padding:16px 18px;text-align:left;transition:box-shadow .15s ease,transform .15s ease}.progression-card:hover{box-shadow:0 4px 16px color-mix(in srgb,var(--_chords-text) 8%,transparent);transform:translateY(-2px)}.prog-header{display:flex;align-items:baseline;gap:8px;margin-bottom:12px;flex-wrap:wrap}.prog-mood{font-size:.95rem;font-weight:700;color:var(--_chords-text)}.prog-name{font-size:.75rem;font-weight:600;color:var(--_chords-muted);text-transform:uppercase;letter-spacing:.06em}.prog-genre{margin-left:auto;font-size:.7rem;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background));font-style:italic}.prog-chords{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:6px}.prog-chord{font-size:1.15rem;font-weight:800;color:var(--_chords-text);letter-spacing:-.02em}.prog-numerals{display:flex;align-items:center;gap:6px;font-size:.75rem;font-style:italic;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.prog-arrow{color:color-mix(in srgb,var(--_chords-muted) 42%,var(--_chords-background));font-size:.8rem}@media(max-width:480px){.page-header h1{font-size:1.5rem}.chord-card{min-width:75px;padding:10px 12px}}\n"] }]
516
+ args: [{ selector: 'the-chords-circle-of-fifths', standalone: true, imports: [NgClass, ChordSectionComponent], host: { '[attr.lang]': 'language()' }, template: "<div class=\"page\">\n <header class=\"page-header\">\n <h1>{{ text().title }}</h1>\n <p>{{ text().subtitle }}</p>\n </header>\n\n <div class=\"circle-wrapper\">\n <svg\n viewBox=\"0 0 600 600\"\n xmlns=\"http://www.w3.org/2000/svg\"\n [attr.aria-label]=\"text().ariaLabel\"\n >\n <!-- \u2500\u2500 Key Signature Ring \u2500\u2500 -->\n @for (key of keys; track key.index) {\n <path [attr.d]=\"arcPath(KEY_SIG.inner, KEY_SIG.outer, key.index)\" class=\"keysig-segment\" />\n <text\n [attr.x]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).y\"\n class=\"keysig-label\"\n >\n {{ getAccidentalText(key) }}\n </text>\n }\n\n <!-- \u2500\u2500 Major Keys Ring \u2500\u2500 -->\n @for (key of keys; track key.index) {\n <g\n [ngClass]=\"['major-group', getMajorState(key.index)]\"\n (click)=\"selectKey(key.index, 'major')\"\n >\n <path [attr.d]=\"arcPath(MAJOR.inner, MAJOR.outer, key.index)\" />\n\n <text\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\n [attr.y]=\"\n textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y +\n (getMajorRole(key.index) ? -9 : 0)\n \"\n class=\"major-label\"\n >\n {{ key.major }}\n </text>\n\n @if (getMajorRole(key.index)) {\n <text\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y + 11\"\n class=\"role-label\"\n >\n {{ getMajorRole(key.index) }}\n </text>\n }\n </g>\n }\n\n <!-- \u2500\u2500 Minor Keys Ring \u2500\u2500 -->\n @for (key of keys; track key.index) {\n <g\n [ngClass]=\"['minor-group', getMinorState(key.index)]\"\n (click)=\"selectKey(key.index, 'minor')\"\n >\n <path [attr.d]=\"arcPath(MINOR.inner, MINOR.outer, key.index)\" />\n\n <text\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\n [attr.y]=\"\n textPos((MINOR.inner + MINOR.outer) / 2, key.index).y +\n (getMinorRole(key.index) ? -8 : 0)\n \"\n class=\"minor-label\"\n >\n {{ key.minor }}\n </text>\n\n @if (getMinorRole(key.index)) {\n <text\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).y + 9\"\n class=\"role-label\"\n >\n {{ getMinorRole(key.index) }}\n </text>\n }\n </g>\n }\n\n <!-- \u2500\u2500 Outer border \u2500\u2500 -->\n <circle cx=\"300\" cy=\"300\" r=\"289\" class=\"outer-border\" />\n\n <!-- \u2500\u2500 Center Circle \u2500\u2500 -->\n <circle cx=\"300\" cy=\"300\" r=\"99\" class=\"center-circle\" />\n\n @if (selectedInfo) {\n <text x=\"300\" y=\"283\" class=\"center-name\">{{ selectedInfo.name }}</text>\n <text x=\"300\" y=\"304\" class=\"center-scale\">{{ selectedInfo.scale }}</text>\n <text x=\"300\" y=\"322\" class=\"center-relative\">{{ selectedInfo.relativeKey }}</text>\n <text x=\"300\" y=\"338\" class=\"center-deselect\">{{ text().deselect }}</text>\n } @else {\n <text x=\"300\" y=\"293\" class=\"center-hint\">{{ text().clickKey }}</text>\n <text x=\"300\" y=\"312\" class=\"center-hint-sm\">{{ text().seeChords }}</text>\n }\n </svg>\n </div>\n\n <!-- \u2500\u2500 Legend \u2500\u2500 -->\n <div class=\"legend\">\n <span class=\"legend-item tonic\">{{ text().tonicLegend }}</span>\n <span class=\"legend-item scale-major\">{{ text().majorLegend }}</span>\n <span class=\"legend-item scale-minor\">{{ text().minorLegend }}</span>\n <span class=\"legend-item scale-diminished\">{{ text().diminishedLegend }}</span>\n </div>\n\n <chord-section [chords]=\"chordNames\" [language]=\"language()\"></chord-section>\n\n <!-- \u2500\u2500 Chord Progressions \u2500\u2500 -->\n @if (progressions.length) {\n <section class=\"progressions-section\">\n <div class=\"progressions-header\">\n <h2>{{ text().commonProgressions }} {{ selectedInfo?.fullName }}</h2>\n <button class=\"btn-randomize\" (click)=\"randomizeProgressions()\">{{ text().generateProgressions }}</button>\n </div>\n <div class=\"progression-list\">\n @for (prog of progressions; track prog.name) {\n <div class=\"progression-card\">\n <div class=\"prog-header\">\n <span class=\"prog-mood\">{{ prog.mood }}</span>\n <span class=\"prog-name\">{{ prog.name }}</span>\n <span class=\"prog-genre\">{{ prog.genre }}</span>\n </div>\n <div class=\"prog-chords\">\n @for (chord of prog.chords; track $index; let last = $last) {\n <span class=\"prog-chord\">{{ chord }}</span>\n @if (!last) {\n <span class=\"prog-arrow\">\u2192</span>\n }\n }\n </div>\n <div class=\"prog-numerals\">\n @for (num of prog.numerals; track $index; let last = $last) {\n <span>{{ num }}</span>\n @if (!last) {\n <span class=\"prog-arrow\">\u2192</span>\n }\n }\n </div>\n <div class=\"prog-expand-row\">\n <button class=\"btn-expand\" (click)=\"toggleCard(prog.name)\">\n {{ expandedCards().has(prog.name) ? text().collapse : text().expand }}\n </button>\n <button class=\"btn-expand\" (click)=\"copyChords(prog)\">\n {{ text().copy }}\n </button>\n </div>\n @if (expandedCards().has(prog.name)) {\n <div class=\"prog-sections\">\n @for (section of prog.sections; track section.label) {\n <div class=\"prog-section\">\n <div class=\"prog-section-label\">{{ section.label }}</div>\n <div class=\"prog-chords\">\n @for (chord of section.chords; track $index; let last = $last) {\n <span class=\"prog-chord\">{{ chord }}</span>\n @if (!last) { <span class=\"prog-arrow\">\u2192</span> }\n }\n </div>\n <div class=\"prog-numerals\">\n @for (num of section.numerals; track $index; let last = $last) {\n <span>{{ num }}</span>\n @if (!last) { <span class=\"prog-arrow\">\u2192</span> }\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </section>\n }\n</div>\n", styles: [":host{--_chords-background: var(--chords-background, #f0f4f8);--_chords-background-alt: var(--chords-background-alt, #f8fafc);--_chords-surface: var(--chords-surface, #ffffff);--_chords-surface-elevated: var(--chords-surface-elevated, #f8fafc);--_chords-text: var(--chords-text, #1e293b);--_chords-muted: var(--chords-muted, #64748b);--_chords-primary: var(--chords-primary, #3b82f6);--_chords-secondary: var(--chords-secondary, #06b6d4);--_chords-highlight: var(--chords-highlight, #f59e0b);--_chords-danger: var(--chords-danger, #a855f7);--_chords-border: var(--chords-border, #e2e8f0);--_chords-on-primary: var(--chords-on-primary, #ffffff);box-sizing:border-box;display:block;min-height:100%;background:var(--_chords-background);color:var(--_chords-text)}:host *,:host *:before,:host *:after{box-sizing:inherit}.page{max-width:720px;margin:0 auto;padding:24px 16px 48px;font-family:Segoe UI,system-ui,sans-serif}.page-header{text-align:center;margin-bottom:28px}.page-header h1{margin:0 0 6px;font-size:2rem;font-weight:800;color:var(--_chords-text);letter-spacing:-.02em}.page-header p{margin:0;color:var(--_chords-muted);font-size:.95rem}.circle-wrapper{max-width:580px;margin:0 auto}.circle-wrapper svg{display:block;width:100%;height:auto;overflow:visible}.keysig-segment{fill:var(--_chords-text)}.keysig-label{fill:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background));font-size:12px;font-weight:500;text-anchor:middle;dominant-baseline:central;pointer-events:none}.major-group{cursor:pointer}.major-group path{fill:color-mix(in srgb,var(--_chords-primary) 14%,var(--_chords-surface));stroke:var(--_chords-surface);stroke-width:2;transition:fill .18s ease}.major-group:hover path{fill:color-mix(in srgb,var(--_chords-primary) 24%,var(--_chords-surface))}.major-group .major-label{fill:var(--_chords-text);font-size:15px;font-weight:700;text-anchor:middle;dominant-baseline:central;pointer-events:none;transition:fill .18s ease}.major-group.tonic path{fill:var(--_chords-highlight)}.major-group.tonic:hover path{fill:color-mix(in srgb,var(--_chords-highlight) 84%,var(--_chords-text))}.major-group.tonic .major-label{fill:var(--_chords-on-primary)}.major-group.scale-major path{fill:var(--_chords-primary)}.major-group.scale-major:hover path{fill:color-mix(in srgb,var(--_chords-primary) 82%,var(--_chords-text))}.major-group.scale-major .major-label{fill:var(--_chords-on-primary)}.minor-group{cursor:pointer}.minor-group path{fill:color-mix(in srgb,var(--_chords-secondary) 10%,var(--_chords-surface));stroke:var(--_chords-surface);stroke-width:2;transition:fill .18s ease}.minor-group:hover path{fill:color-mix(in srgb,var(--_chords-secondary) 20%,var(--_chords-surface))}.minor-group .minor-label{fill:color-mix(in srgb,var(--_chords-text) 78%,var(--_chords-muted));font-size:12px;font-weight:600;text-anchor:middle;dominant-baseline:central;pointer-events:none;transition:fill .18s ease}.minor-group.tonic path{fill:var(--_chords-highlight)}.minor-group.tonic:hover path{fill:color-mix(in srgb,var(--_chords-highlight) 84%,var(--_chords-text))}.minor-group.tonic .minor-label{fill:var(--_chords-on-primary)}.minor-group.scale-minor path{fill:var(--_chords-secondary)}.minor-group.scale-minor:hover path{fill:color-mix(in srgb,var(--_chords-secondary) 82%,var(--_chords-text))}.minor-group.scale-minor .minor-label{fill:var(--_chords-on-primary)}.minor-group.scale-diminished path{fill:var(--_chords-danger)}.minor-group.scale-diminished:hover path{fill:color-mix(in srgb,var(--_chords-danger) 82%,var(--_chords-text))}.minor-group.scale-diminished .minor-label{fill:var(--_chords-on-primary)}.role-label{font-size:10px;font-style:italic;font-weight:600;text-anchor:middle;dominant-baseline:central;pointer-events:none;fill:color-mix(in srgb,var(--_chords-on-primary) 85%,transparent)}.outer-border{fill:none;stroke:var(--_chords-text);stroke-width:2.5}.center-circle{fill:var(--_chords-surface);stroke:var(--_chords-border);stroke-width:2;filter:drop-shadow(0 2px 10px color-mix(in srgb,var(--_chords-text) 8%,transparent))}.center-name{font-size:30px;font-weight:800;text-anchor:middle;dominant-baseline:auto;fill:var(--_chords-text);letter-spacing:-.02em}.center-scale{font-size:13px;font-weight:600;text-anchor:middle;dominant-baseline:auto;fill:var(--_chords-muted);text-transform:uppercase;letter-spacing:.08em}.center-relative{font-size:9.5px;font-weight:500;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.center-deselect{font-size:8px;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 42%,var(--_chords-background))}.center-hint{font-size:16px;font-weight:600;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.center-hint-sm{font-size:11px;text-anchor:middle;dominant-baseline:auto;fill:color-mix(in srgb,var(--_chords-muted) 42%,var(--_chords-background))}.legend{display:flex;justify-content:center;gap:16px;flex-wrap:wrap;margin:16px 0 0}.legend .legend-item{display:flex;align-items:center;gap:6px;font-size:.8rem;color:var(--_chords-muted)}.legend .legend-item:before{content:\"\";display:inline-block;width:14px;height:14px;border-radius:3px}.legend .legend-item.tonic:before{background:var(--_chords-highlight)}.legend .legend-item.scale-major:before{background:var(--_chords-primary)}.legend .legend-item.scale-minor:before{background:var(--_chords-secondary)}.legend .legend-item.scale-diminished:before{background:var(--_chords-danger)}.progressions-section{margin-top:36px;text-align:center}.progressions-header{display:flex;align-items:center;justify-content:center;gap:12px;margin-bottom:20px;flex-wrap:wrap}.progressions-header h2{margin:0;font-size:1.25rem;font-weight:700;color:var(--_chords-text)}.btn-randomize{padding:6px 14px;font-size:.8rem;font-weight:600;border:1.5px solid var(--_chords-primary);border-radius:20px;background:transparent;color:var(--_chords-primary);cursor:pointer;transition:background .15s,color .15s;white-space:nowrap}.btn-randomize:hover{background:var(--_chords-primary);color:var(--_chords-on-primary)}.progression-list{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}@media(max-width:480px){.progression-list{grid-template-columns:1fr}}.progression-card{background:var(--_chords-surface);border:1.5px solid var(--_chords-border);border-radius:14px;padding:16px 18px;text-align:left;transition:box-shadow .15s ease,transform .15s ease}.progression-card:hover{box-shadow:0 4px 16px color-mix(in srgb,var(--_chords-text) 8%,transparent);transform:translateY(-2px)}.prog-header{display:flex;align-items:baseline;gap:8px;margin-bottom:12px;flex-wrap:wrap}.prog-mood{font-size:.95rem;font-weight:700;color:var(--_chords-text)}.prog-name{font-size:.75rem;font-weight:600;color:var(--_chords-muted);text-transform:uppercase;letter-spacing:.06em}.prog-genre{margin-left:auto;font-size:.7rem;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background));font-style:italic}.prog-chords{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:6px}.prog-chord{font-size:1.15rem;font-weight:800;color:var(--_chords-text);letter-spacing:-.02em}.prog-numerals{display:flex;align-items:center;gap:6px;font-size:.75rem;font-style:italic;color:color-mix(in srgb,var(--_chords-muted) 72%,var(--_chords-background))}.prog-expand-row{display:flex;justify-content:flex-end;margin-top:10px}.btn-expand{padding:3px 10px;font-size:.72rem;font-weight:600;border:1px solid var(--_chords-border);border-radius:12px;background:transparent;color:var(--_chords-muted);cursor:pointer;transition:border-color .15s,color .15s}.btn-expand:hover{border-color:var(--_chords-primary);color:var(--_chords-primary)}.prog-sections{margin-top:12px;border-top:1px solid var(--_chords-border);padding-top:10px;display:flex;flex-direction:column;gap:10px}.prog-section{padding:8px 10px;border-radius:8px;background:color-mix(in srgb,var(--_chords-text) 4%,var(--_chords-surface))}.prog-section-label{font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--_chords-muted);margin-bottom:5px}.prog-arrow{color:color-mix(in srgb,var(--_chords-muted) 42%,var(--_chords-background));font-size:.8rem}@media(max-width:480px){.page-header h1{font-size:1.5rem}}\n"] }]
427
517
  }], propDecorators: { language: [{ type: i0.Input, args: [{ isSignal: true, alias: "language", required: false }] }] } });
428
518
 
429
519
  /*
@@ -434,5 +524,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
434
524
  * Generated bundle index. Do not edit.
435
525
  */
436
526
 
437
- export { CircleOfFifthsComponent };
527
+ export { ChordSectionComponent, CircleOfFifthsComponent };
438
528
  //# sourceMappingURL=gblp-circle-of-fifths.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"gblp-circle-of-fifths.mjs","sources":["../../../projects/circle-of-fifths-lib/src/lib/circle-of-fifths.ts","../../../projects/circle-of-fifths-lib/src/lib/circle-of-fifths.html","../../../projects/circle-of-fifths-lib/src/public-api.ts","../../../projects/circle-of-fifths-lib/src/gblp-circle-of-fifths.ts"],"sourcesContent":["import { Component, computed, input, signal } from '@angular/core';\nimport { NgClass } from '@angular/common';\n\ninterface CircleKey {\n index: number;\n major: string;\n minor: string;\n sharps: number;\n flats: number;\n}\n\ninterface ChordRow {\n numeral: string;\n chord: string;\n chordType: string;\n role: string;\n state: 'tonic' | 'scale-major' | 'scale-minor' | 'scale-diminished';\n}\n\ninterface Progression {\n name: string;\n mood: string;\n genre: string;\n numerals: string[];\n chords: string[];\n}\n\ntype Language = 'en' | 'es';\ntype LocalizedText = Record<Language, string>;\n\ninterface ProgressionDefinition {\n name: LocalizedText;\n mood: LocalizedText;\n genre: LocalizedText;\n numerals: string[];\n}\n\nconst PROGRESSIONS: Record<'major' | 'minor', ProgressionDefinition[]> = {\n major: [\n {\n name: { en: 'Anthemic', es: 'Épica' },\n mood: { en: '😄 Joyful', es: '😄 Alegre' },\n genre: { en: 'Folk, Rock', es: 'Folk, rock' },\n numerals: ['I', 'IV', 'V', 'I'],\n },\n {\n name: { en: 'Hopeful', es: 'Esperanzadora' },\n mood: { en: '✨ Hopeful', es: '✨ Esperanzadora' },\n genre: { en: 'Pop, Indie', es: 'Pop, indie' },\n numerals: ['I', 'V', 'vi', 'IV'],\n },\n {\n name: { en: 'Melancholic', es: 'Melancólica' },\n mood: { en: '😢 Emotional', es: '😢 Emocional' },\n genre: { en: 'Ballads, Pop', es: 'Baladas, pop' },\n numerals: ['vi', 'IV', 'I', 'V'],\n },\n {\n name: { en: 'Cinematic', es: 'Cinemática' },\n mood: { en: '🎬 Tense', es: '🎬 Tensa' },\n genre: { en: 'Film, Jazz', es: 'Cine, jazz' },\n numerals: ['ii', 'V', 'vii°', 'I'],\n },\n ],\n minor: [\n {\n name: { en: 'Brooding', es: 'Sombría' },\n mood: { en: '🌑 Dark', es: '🌑 Oscura' },\n genre: { en: 'Rock, Metal', es: 'Rock, metal' },\n numerals: ['i', 'VII', 'VI', 'VII'],\n },\n {\n name: { en: 'Haunting', es: 'Inquietante' },\n mood: { en: '👻 Mysterious', es: '👻 Misteriosa' },\n genre: { en: 'Film, Gothic', es: 'Cine, gótico' },\n numerals: ['i', 'iv', 'VII', 'III'],\n },\n {\n name: { en: 'Driving', es: 'Impulsora' },\n mood: { en: '⚡ Urgent', es: '⚡ Urgente' },\n genre: { en: 'Pop, EDM', es: 'Pop, EDM' },\n numerals: ['i', 'VI', 'III', 'VII'],\n },\n {\n name: { en: 'Wistful', es: 'Nostálgica' },\n mood: { en: '🌙 Wistful', es: '🌙 Nostálgica' },\n genre: { en: 'Cinematic', es: 'Cinemática' },\n numerals: ['i', 'v', 'VI', 'VII'],\n },\n ],\n};\n\nconst COPY = {\n en: {\n title: 'Circle of Fifths',\n subtitle: 'Click any major or minor key to highlight its diatonic chords',\n ariaLabel: 'Circle of Fifths',\n clickKey: 'Click a key',\n seeChords: 'to see its chords',\n deselect: '↩ click to deselect',\n tonicLegend: 'Tonic (I / i)',\n majorLegend: 'Major chords (IV, V)',\n minorLegend: 'Minor chords (ii, iii, vi)',\n diminishedLegend: 'Diminished (vii° / ii°)',\n diatonicChords: 'Diatonic Chords',\n commonProgressions: 'Common Progressions in',\n major: 'Major',\n minor: 'Minor',\n diminished: 'diminished',\n relativeMinor: 'Relative minor',\n relativeMajor: 'Relative major',\n roles: {\n tonic: 'Tonic',\n supertonic: 'Supertonic',\n mediant: 'Mediant',\n subdominant: 'Subdominant',\n dominant: 'Dominant',\n submediant: 'Submediant',\n leadingTone: 'Leading Tone',\n subtonic: 'Subtonic',\n },\n },\n es: {\n title: 'Círculo de quintas',\n subtitle: 'Haz clic en una tonalidad mayor o menor para resaltar sus acordes diatónicos',\n ariaLabel: 'Círculo de quintas',\n clickKey: 'Haz clic en una tonalidad',\n seeChords: 'para ver sus acordes',\n deselect: '↩ haz clic para deseleccionar',\n tonicLegend: 'Tónica (I / i)',\n majorLegend: 'Acordes mayores (IV, V)',\n minorLegend: 'Acordes menores (ii, iii, vi)',\n diminishedLegend: 'Disminuido (vii° / ii°)',\n diatonicChords: 'Acordes diatónicos',\n commonProgressions: 'Progresiones comunes en',\n major: 'Mayor',\n minor: 'Menor',\n diminished: 'disminuido',\n relativeMinor: 'Relativa menor',\n relativeMajor: 'Relativa mayor',\n roles: {\n tonic: 'Tónica',\n supertonic: 'Supertónica',\n mediant: 'Mediante',\n subdominant: 'Subdominante',\n dominant: 'Dominante',\n submediant: 'Submediante',\n leadingTone: 'Sensible',\n subtonic: 'Subtónica',\n },\n },\n} as const;\n\n@Component({\n selector: 'the-chords-circle-of-fifths',\n standalone: true,\n imports: [NgClass],\n templateUrl: './circle-of-fifths.html',\n styleUrl: './circle-of-fifths.scss',\n host: { '[attr.lang]': 'language()' },\n})\nexport class CircleOfFifthsComponent {\n readonly language = input<Language>('en');\n readonly text = computed(() => COPY[this.language()]);\n readonly CX = 300;\n readonly CY = 300;\n\n readonly KEY_SIG = { inner: 250, outer: 288 };\n readonly MAJOR = { inner: 172, outer: 250 };\n readonly MINOR = { inner: 102, outer: 172 };\n\n readonly keys: CircleKey[] = [\n { index: 0, major: 'C', minor: 'Am', sharps: 0, flats: 0 },\n { index: 1, major: 'G', minor: 'Em', sharps: 1, flats: 0 },\n { index: 2, major: 'D', minor: 'Bm', sharps: 2, flats: 0 },\n { index: 3, major: 'A', minor: 'F♯m', sharps: 3, flats: 0 },\n { index: 4, major: 'E', minor: 'C♯m', sharps: 4, flats: 0 },\n { index: 5, major: 'B', minor: 'G♯m', sharps: 5, flats: 0 },\n { index: 6, major: 'F♯', minor: 'D♯m', sharps: 6, flats: 0 },\n { index: 7, major: 'D♭', minor: 'B♭m', sharps: 0, flats: 5 },\n { index: 8, major: 'A♭', minor: 'Fm', sharps: 0, flats: 4 },\n { index: 9, major: 'E♭', minor: 'Cm', sharps: 0, flats: 3 },\n { index: 10, major: 'B♭', minor: 'Gm', sharps: 0, flats: 2 },\n { index: 11, major: 'F', minor: 'Dm', sharps: 0, flats: 1 },\n ];\n\n selectedIndex = signal<number | null>(null);\n selectedType = signal<'major' | 'minor' | null>(null);\n\n private get slice() {\n const idx = this.selectedIndex();\n if (idx === null) return null;\n return {\n prev: (idx - 1 + 12) % 12,\n curr: idx,\n next: (idx + 1) % 12,\n };\n }\n\n selectKey(index: number, type: 'major' | 'minor'): void {\n if (this.selectedIndex() === index && this.selectedType() === type) {\n this.selectedIndex.set(null);\n this.selectedType.set(null);\n } else {\n this.selectedIndex.set(index);\n this.selectedType.set(type);\n }\n }\n\n getMajorState(index: number): string {\n const s = this.slice;\n if (!s) return '';\n const type = this.selectedType();\n if (index === s.curr) return type === 'major' ? 'tonic' : 'scale-major';\n if (index === s.prev || index === s.next) return 'scale-major';\n return '';\n }\n\n getMinorState(index: number): string {\n const s = this.slice;\n if (!s) return '';\n const type = this.selectedType();\n const dimIdx = (s.curr + 2) % 12;\n if (index === s.curr) return type === 'minor' ? 'tonic' : 'scale-minor';\n if (index === s.prev || index === s.next) return 'scale-minor';\n if (index === dimIdx) return 'scale-diminished';\n return '';\n }\n\n getMajorRole(index: number): string {\n const s = this.slice;\n if (!s) return '';\n const type = this.selectedType();\n if (type === 'major') {\n if (index === s.curr) return 'I';\n if (index === s.prev) return 'IV';\n if (index === s.next) return 'V';\n } else {\n if (index === s.curr) return 'III';\n if (index === s.prev) return 'VI';\n if (index === s.next) return 'VII';\n }\n return '';\n }\n\n getMinorRole(index: number): string {\n const s = this.slice;\n if (!s) return '';\n const type = this.selectedType();\n const dimIdx = (s.curr + 2) % 12;\n if (type === 'major') {\n if (index === s.curr) return 'vi';\n if (index === s.prev) return 'ii';\n if (index === s.next) return 'iii';\n if (index === dimIdx) return 'vii°';\n } else {\n if (index === s.curr) return 'i';\n if (index === s.prev) return 'iv';\n if (index === s.next) return 'v';\n if (index === dimIdx) return 'ii°';\n }\n return '';\n }\n\n arcPath(innerR: number, outerR: number, index: number, gap = 1.5): string {\n const mid = index * 30 - 90;\n const s = (mid - 15 + gap) * (Math.PI / 180);\n const e = (mid + 15 - gap) * (Math.PI / 180);\n const cx = this.CX,\n cy = this.CY;\n const x1 = cx + outerR * Math.cos(s),\n y1 = cy + outerR * Math.sin(s);\n const x2 = cx + outerR * Math.cos(e),\n y2 = cy + outerR * Math.sin(e);\n const x3 = cx + innerR * Math.cos(e),\n y3 = cy + innerR * Math.sin(e);\n const x4 = cx + innerR * Math.cos(s),\n y4 = cy + innerR * Math.sin(s);\n const f = (n: number) => n.toFixed(2);\n return `M${f(x1)} ${f(y1)} A${outerR} ${outerR} 0 0 1 ${f(x2)} ${f(y2)} L${f(x3)} ${f(y3)} A${innerR} ${innerR} 0 0 0 ${f(x4)} ${f(y4)}Z`;\n }\n\n textPos(midR: number, index: number): { x: number; y: number } {\n const a = (index * 30 - 90) * (Math.PI / 180);\n return {\n x: this.CX + midR * Math.cos(a),\n y: this.CY + midR * Math.sin(a),\n };\n }\n\n private dimChord(idx: number): string {\n // The diminished chord root is the minor key two steps clockwise (idx+2)\n const minor = this.keys[(idx + 2) % 12].minor;\n return minor.replace(/m$/, '°');\n }\n\n getAccidentalText(key: CircleKey): string {\n if (key.sharps > 0) return `${key.sharps}♯`;\n if (key.flats > 0) return `${key.flats}♭`;\n return '—';\n }\n\n get selectedInfo() {\n const idx = this.selectedIndex();\n const type = this.selectedType();\n if (idx === null || type === null) return null;\n const key = this.keys[idx];\n const copy = this.text();\n const scale = type === 'major' ? copy.major : copy.minor;\n return {\n name: type === 'major' ? key.major : key.minor,\n scale,\n fullName: `${type === 'major' ? key.major : key.minor} ${scale}`,\n relativeKey:\n type === 'major'\n ? `${copy.relativeMinor}: ${key.minor}`\n : `${copy.relativeMajor}: ${key.major}`,\n };\n }\n\n get chordTable(): ChordRow[] {\n const idx = this.selectedIndex();\n const type = this.selectedType();\n if (idx === null || type === null) return [];\n\n const prev = (idx - 1 + 12) % 12;\n const next = (idx + 1) % 12;\n const copy = this.text();\n\n if (type === 'major') {\n return [\n {\n numeral: 'I',\n chord: this.keys[idx].major,\n chordType: copy.major,\n role: copy.roles.tonic,\n state: 'tonic',\n },\n {\n numeral: 'ii',\n chord: this.keys[prev].minor,\n chordType: copy.minor,\n role: copy.roles.supertonic,\n state: 'scale-minor',\n },\n {\n numeral: 'iii',\n chord: this.keys[next].minor,\n chordType: copy.minor,\n role: copy.roles.mediant,\n state: 'scale-minor',\n },\n {\n numeral: 'IV',\n chord: this.keys[prev].major,\n chordType: copy.major,\n role: copy.roles.subdominant,\n state: 'scale-major',\n },\n {\n numeral: 'V',\n chord: this.keys[next].major,\n chordType: copy.major,\n role: copy.roles.dominant,\n state: 'scale-major',\n },\n {\n numeral: 'vi',\n chord: this.keys[idx].minor,\n chordType: copy.minor,\n role: copy.roles.submediant,\n state: 'scale-minor',\n },\n {\n numeral: 'vii°',\n chord: this.dimChord(idx),\n chordType: copy.diminished,\n role: copy.roles.leadingTone,\n state: 'scale-diminished',\n },\n ];\n } else {\n return [\n {\n numeral: 'i',\n chord: this.keys[idx].minor,\n chordType: copy.minor,\n role: copy.roles.tonic,\n state: 'tonic',\n },\n {\n numeral: 'iv',\n chord: this.keys[prev].minor,\n chordType: copy.minor,\n role: copy.roles.subdominant,\n state: 'scale-minor',\n },\n {\n numeral: 'v',\n chord: this.keys[next].minor,\n chordType: copy.minor,\n role: copy.roles.dominant,\n state: 'scale-minor',\n },\n {\n numeral: 'III',\n chord: this.keys[idx].major,\n chordType: copy.major,\n role: copy.roles.mediant,\n state: 'scale-major',\n },\n {\n numeral: 'VI',\n chord: this.keys[prev].major,\n chordType: copy.major,\n role: copy.roles.submediant,\n state: 'scale-major',\n },\n {\n numeral: 'VII',\n chord: this.keys[next].major,\n chordType: copy.major,\n role: copy.roles.subtonic,\n state: 'scale-major',\n },\n {\n numeral: 'ii°',\n chord: this.dimChord(idx),\n chordType: copy.diminished,\n role: copy.roles.supertonic,\n state: 'scale-diminished',\n },\n ];\n }\n }\n\n get progressions(): Progression[] {\n const table = this.chordTable;\n if (!table.length) return [];\n const lookup = new Map(table.map((r) => [r.numeral, r.chord]));\n const language = this.language();\n const defs = PROGRESSIONS[this.selectedType() === 'major' ? 'major' : 'minor'];\n return defs.map((definition) => ({\n name: definition.name[language],\n mood: definition.mood[language],\n genre: definition.genre[language],\n numerals: definition.numerals,\n chords: definition.numerals.map((numeral) => lookup.get(numeral) ?? numeral),\n }));\n }\n}\n","<div class=\"page\">\n <header class=\"page-header\">\n <h1>{{ text().title }}</h1>\n <p>{{ text().subtitle }}</p>\n </header>\n\n <div class=\"circle-wrapper\">\n <svg\n viewBox=\"0 0 600 600\"\n xmlns=\"http://www.w3.org/2000/svg\"\n [attr.aria-label]=\"text().ariaLabel\"\n >\n <!-- ── Key Signature Ring ── -->\n @for (key of keys; track key.index) {\n <path [attr.d]=\"arcPath(KEY_SIG.inner, KEY_SIG.outer, key.index)\" class=\"keysig-segment\" />\n <text\n [attr.x]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).y\"\n class=\"keysig-label\"\n >\n {{ getAccidentalText(key) }}\n </text>\n }\n\n <!-- ── Major Keys Ring ── -->\n @for (key of keys; track key.index) {\n <g\n [ngClass]=\"['major-group', getMajorState(key.index)]\"\n (click)=\"selectKey(key.index, 'major')\"\n >\n <path [attr.d]=\"arcPath(MAJOR.inner, MAJOR.outer, key.index)\" />\n\n <text\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\n [attr.y]=\"\n textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y +\n (getMajorRole(key.index) ? -9 : 0)\n \"\n class=\"major-label\"\n >\n {{ key.major }}\n </text>\n\n @if (getMajorRole(key.index)) {\n <text\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y + 11\"\n class=\"role-label\"\n >\n {{ getMajorRole(key.index) }}\n </text>\n }\n </g>\n }\n\n <!-- ── Minor Keys Ring ── -->\n @for (key of keys; track key.index) {\n <g\n [ngClass]=\"['minor-group', getMinorState(key.index)]\"\n (click)=\"selectKey(key.index, 'minor')\"\n >\n <path [attr.d]=\"arcPath(MINOR.inner, MINOR.outer, key.index)\" />\n\n <text\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\n [attr.y]=\"\n textPos((MINOR.inner + MINOR.outer) / 2, key.index).y +\n (getMinorRole(key.index) ? -8 : 0)\n \"\n class=\"minor-label\"\n >\n {{ key.minor }}\n </text>\n\n @if (getMinorRole(key.index)) {\n <text\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).y + 9\"\n class=\"role-label\"\n >\n {{ getMinorRole(key.index) }}\n </text>\n }\n </g>\n }\n\n <!-- ── Outer border ── -->\n <circle cx=\"300\" cy=\"300\" r=\"289\" class=\"outer-border\" />\n\n <!-- ── Center Circle ── -->\n <circle cx=\"300\" cy=\"300\" r=\"99\" class=\"center-circle\" />\n\n @if (selectedInfo) {\n <text x=\"300\" y=\"283\" class=\"center-name\">{{ selectedInfo.name }}</text>\n <text x=\"300\" y=\"304\" class=\"center-scale\">{{ selectedInfo.scale }}</text>\n <text x=\"300\" y=\"322\" class=\"center-relative\">{{ selectedInfo.relativeKey }}</text>\n <text x=\"300\" y=\"338\" class=\"center-deselect\">{{ text().deselect }}</text>\n } @else {\n <text x=\"300\" y=\"293\" class=\"center-hint\">{{ text().clickKey }}</text>\n <text x=\"300\" y=\"312\" class=\"center-hint-sm\">{{ text().seeChords }}</text>\n }\n </svg>\n </div>\n\n <!-- ── Legend ── -->\n <div class=\"legend\">\n <span class=\"legend-item tonic\">{{ text().tonicLegend }}</span>\n <span class=\"legend-item scale-major\">{{ text().majorLegend }}</span>\n <span class=\"legend-item scale-minor\">{{ text().minorLegend }}</span>\n <span class=\"legend-item scale-diminished\">{{ text().diminishedLegend }}</span>\n </div>\n\n <!-- ── Chord Table ── -->\n @if (selectedInfo && chordTable.length) {\n <section class=\"chord-section\">\n <h2>{{ selectedInfo.fullName }} — {{ text().diatonicChords }}</h2>\n <p class=\"relative-label\">{{ selectedInfo.relativeKey }}</p>\n <div class=\"chord-grid\">\n @for (row of chordTable; track row.numeral) {\n <div class=\"chord-card\" [ngClass]=\"row.state\">\n <div class=\"chord-numeral\">{{ row.numeral }}</div>\n <div class=\"chord-name\">{{ row.chord }}</div>\n <div class=\"chord-type\">{{ row.chordType }}</div>\n <div class=\"chord-role\">{{ row.role }}</div>\n </div>\n }\n </div>\n </section>\n }\n\n <!-- ── Chord Progressions ── -->\n @if (progressions.length) {\n <section class=\"progressions-section\">\n <h2>{{ text().commonProgressions }} {{ selectedInfo?.fullName }}</h2>\n <div class=\"progression-list\">\n @for (prog of progressions; track prog.name) {\n <div class=\"progression-card\">\n <div class=\"prog-header\">\n <span class=\"prog-mood\">{{ prog.mood }}</span>\n <span class=\"prog-name\">{{ prog.name }}</span>\n <span class=\"prog-genre\">{{ prog.genre }}</span>\n </div>\n <div class=\"prog-chords\">\n @for (chord of prog.chords; track $index; let last = $last) {\n <span class=\"prog-chord\">{{ chord }}</span>\n @if (!last) {\n <span class=\"prog-arrow\">→</span>\n }\n }\n </div>\n <div class=\"prog-numerals\">\n @for (num of prog.numerals; track $index; let last = $last) {\n <span>{{ num }}</span>\n @if (!last) {\n <span class=\"prog-arrow\">→</span>\n }\n }\n </div>\n </div>\n }\n </div>\n </section>\n }\n</div>\n","/*\n * Public API Surface of circle-of-fifths-lib\n */\n\nexport * from './lib/circle-of-fifths';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gblp-circle-of-fifths.mjs","sources":["../../../projects/circle-of-fifths-lib/src/lib/circle-of-fifths.ts","../../../projects/circle-of-fifths-lib/src/lib/chord-section.component.html","../../../projects/circle-of-fifths-lib/src/lib/circle-of-fifths.html","../../../projects/circle-of-fifths-lib/src/public-api.ts","../../../projects/circle-of-fifths-lib/src/gblp-circle-of-fifths.ts"],"sourcesContent":["import { Component, computed, input, signal } from '@angular/core';\nimport { NgClass } from '@angular/common';\n\ninterface CircleKey {\n index: number;\n major: string;\n minor: string;\n sharps: number;\n flats: number;\n}\n\ninterface ChordRow {\n numeral: string;\n chord: string;\n chordType: string;\n role: string;\n state: 'tonic' | 'scale-major' | 'scale-minor' | 'scale-diminished';\n}\n\ninterface Section {\n label: string;\n numerals: string[];\n chords: string[];\n}\n\ninterface Progression {\n name: string;\n mood: string;\n genre: string;\n numerals: string[];\n chords: string[];\n sections: Section[];\n}\n\ntype Language = 'en' | 'es';\ntype LocalizedText = Record<Language, string>;\n\ninterface ProgressionDefinition {\n name: LocalizedText;\n mood: LocalizedText;\n genre: LocalizedText;\n numerals: string[];\n}\n\nconst PROGRESSIONS: Record<'major' | 'minor', ProgressionDefinition[]> = {\n major: [\n {\n name: { en: 'Anthemic', es: 'Épica' },\n mood: { en: '😄 Joyful', es: '😄 Alegre' },\n genre: { en: 'Folk, Rock', es: 'Folk, rock' },\n numerals: ['I', 'IV', 'V', 'I'],\n },\n {\n name: { en: 'Hopeful', es: 'Esperanzadora' },\n mood: { en: '✨ Hopeful', es: '✨ Esperanzadora' },\n genre: { en: 'Pop, Indie', es: 'Pop, indie' },\n numerals: ['I', 'V', 'vi', 'IV'],\n },\n {\n name: { en: 'Melancholic', es: 'Melancólica' },\n mood: { en: '😢 Emotional', es: '😢 Emocional' },\n genre: { en: 'Ballads, Pop', es: 'Baladas, pop' },\n numerals: ['vi', 'IV', 'I', 'V'],\n },\n {\n name: { en: 'Cinematic', es: 'Cinemática' },\n mood: { en: '🎬 Tense', es: '🎬 Tensa' },\n genre: { en: 'Film, Jazz', es: 'Cine, jazz' },\n numerals: ['ii', 'V', 'vii°', 'I'],\n },\n {\n name: { en: 'Nostalgic', es: 'Retro' },\n mood: { en: '🕺 Retro', es: '🕺 Retro' },\n genre: { en: 'Doo-Wop, Pop', es: 'Doo-Wop, pop' },\n numerals: ['I', 'vi', 'IV', 'V'],\n },\n {\n name: { en: 'Dreamy', es: 'Soñadora' },\n mood: { en: '🌸 Floaty', es: '🌸 Etérea' },\n genre: { en: 'J-Pop, Anime', es: 'J-Pop, anime' },\n numerals: ['IV', 'V', 'iii', 'vi'],\n },\n {\n name: { en: 'Gospel', es: 'Gospel' },\n mood: { en: '🙏 Uplifting', es: '🙏 Elevadora' },\n genre: { en: 'Gospel, Soul', es: 'Gospel, soul' },\n numerals: ['I', 'IV', 'ii', 'V'],\n },\n {\n name: { en: 'Bittersweet', es: 'Agridulce' },\n mood: { en: '🍂 Bittersweet', es: '🍂 Agridulce' },\n genre: { en: 'Alt Rock, Indie', es: 'Rock alternativo, indie' },\n numerals: ['I', 'iii', 'IV', 'V'],\n },\n ],\n minor: [\n {\n name: { en: 'Brooding', es: 'Sombría' },\n mood: { en: '🌑 Dark', es: '🌑 Oscura' },\n genre: { en: 'Rock, Metal', es: 'Rock, metal' },\n numerals: ['i', 'VII', 'VI', 'VII'],\n },\n {\n name: { en: 'Haunting', es: 'Inquietante' },\n mood: { en: '👻 Mysterious', es: '👻 Misteriosa' },\n genre: { en: 'Film, Gothic', es: 'Cine, gótico' },\n numerals: ['i', 'iv', 'VII', 'III'],\n },\n {\n name: { en: 'Driving', es: 'Impulsora' },\n mood: { en: '⚡ Urgent', es: '⚡ Urgente' },\n genre: { en: 'Pop, EDM', es: 'Pop, EDM' },\n numerals: ['i', 'VI', 'III', 'VII'],\n },\n {\n name: { en: 'Wistful', es: 'Nostálgica' },\n mood: { en: '🌙 Wistful', es: '🌙 Nostálgica' },\n genre: { en: 'Cinematic', es: 'Cinemática' },\n numerals: ['i', 'v', 'VI', 'VII'],\n },\n {\n name: { en: 'Flamenco', es: 'Flamenco' },\n mood: { en: '🌹 Passionate', es: '🌹 Apasionado' },\n genre: { en: 'Flamenco, World', es: 'Flamenco, mundial' },\n numerals: ['i', 'VII', 'VI', 'iv'],\n },\n {\n name: { en: 'Tragic', es: 'Trágica' },\n mood: { en: '💔 Tragic', es: '💔 Trágica' },\n genre: { en: 'Classical, Drama', es: 'Clásica, drama' },\n numerals: ['i', 'iv', 'v', 'i'],\n },\n {\n name: { en: 'Epic', es: 'Épica' },\n mood: { en: '⚔️ Heroic', es: '⚔️ Heroico' },\n genre: { en: 'Epic, Orchestral', es: 'Épica, orquestal' },\n numerals: ['i', 'III', 'VII', 'VI'],\n },\n {\n name: { en: 'Ethereal', es: 'Etérea' },\n mood: { en: '🌊 Hypnotic', es: '🌊 Hipnótica' },\n genre: { en: 'Ambient, Post-Rock', es: 'Ambient, post-rock' },\n numerals: ['i', 'VI', 'iv', 'VII'],\n },\n ],\n};\n\nconst COPY = {\n en: {\n title: 'Circle of Fifths',\n subtitle: 'Click any major or minor key to highlight its diatonic chords',\n ariaLabel: 'Circle of Fifths',\n clickKey: 'Click a key',\n seeChords: 'to see its chords',\n deselect: '↩ click to deselect',\n tonicLegend: 'Tonic (I / i)',\n majorLegend: 'Major chords (IV, V)',\n minorLegend: 'Minor chords (ii, iii, vi)',\n diminishedLegend: 'Diminished (vii° / ii°)',\n diatonicChords: 'Diatonic Chords',\n commonProgressions: 'Common Progressions in',\n generateProgressions: 'Shuffle',\n chorus: 'Chorus',\n bridge: 'Bridge',\n outro: 'Outro',\n verse: 'Verse',\n copy: 'Copy',\n expand: 'Expand',\n collapse: 'Collapse',\n major: 'Major',\n minor: 'Minor',\n diminished: 'diminished',\n relativeMinor: 'Relative minor',\n relativeMajor: 'Relative major',\n roles: {\n tonic: 'Tonic',\n supertonic: 'Supertonic',\n mediant: 'Mediant',\n subdominant: 'Subdominant',\n dominant: 'Dominant',\n submediant: 'Submediant',\n leadingTone: 'Leading Tone',\n subtonic: 'Subtonic',\n },\n },\n es: {\n title: 'Círculo de quintas',\n subtitle: 'Haz clic en una tonalidad mayor o menor para resaltar sus acordes diatónicos',\n ariaLabel: 'Círculo de quintas',\n clickKey: 'Haz clic en una tonalidad',\n seeChords: 'para ver sus acordes',\n deselect: '↩ haz clic para deseleccionar',\n tonicLegend: 'Tónica (I / i)',\n majorLegend: 'Acordes mayores (IV, V)',\n minorLegend: 'Acordes menores (ii, iii, vi)',\n diminishedLegend: 'Disminuido (vii° / ii°)',\n diatonicChords: 'Acordes diatónicos',\n commonProgressions: 'Progresiones comunes en',\n generateProgressions: 'Generar nuevos',\n chorus: 'Coro',\n bridge: 'Puente',\n outro: 'Final',\n verse: 'Estrofa',\n copy: 'Copiar',\n expand: 'Expandir',\n collapse: 'Colapsar',\n major: 'Mayor',\n minor: 'Menor',\n diminished: 'disminuido',\n relativeMinor: 'Relativa menor',\n relativeMajor: 'Relativa mayor',\n roles: {\n tonic: 'Tónica',\n supertonic: 'Supertónica',\n mediant: 'Mediante',\n subdominant: 'Subdominante',\n dominant: 'Dominante',\n submediant: 'Submediante',\n leadingTone: 'Sensible',\n subtonic: 'Subtónica',\n },\n },\n} as const;\n\nconst KEYS: CircleKey[] = [\n { index: 0, major: 'C', minor: 'Am', sharps: 0, flats: 0 },\n { index: 1, major: 'G', minor: 'Em', sharps: 1, flats: 0 },\n { index: 2, major: 'D', minor: 'Bm', sharps: 2, flats: 0 },\n { index: 3, major: 'A', minor: 'F♯m', sharps: 3, flats: 0 },\n { index: 4, major: 'E', minor: 'C♯m', sharps: 4, flats: 0 },\n { index: 5, major: 'B', minor: 'G♯m', sharps: 5, flats: 0 },\n { index: 6, major: 'F♯', minor: 'D♯m', sharps: 6, flats: 0 },\n { index: 7, major: 'D♭', minor: 'B♭m', sharps: 0, flats: 5 },\n { index: 8, major: 'A♭', minor: 'Fm', sharps: 0, flats: 4 },\n { index: 9, major: 'E♭', minor: 'Cm', sharps: 0, flats: 3 },\n { index: 10, major: 'B♭', minor: 'Gm', sharps: 0, flats: 2 },\n { index: 11, major: 'F', minor: 'Dm', sharps: 0, flats: 1 },\n];\n\nfunction dimChord(idx: number): string {\n // The diminished chord root is the minor key two steps clockwise (idx+2)\n return KEYS[(idx + 2) % 12].minor.replace(/m$/, '°');\n}\n\nfunction buildChordRows(idx: number, type: 'major' | 'minor', copy: (typeof COPY)[Language]): ChordRow[] {\n const prev = (idx - 1 + 12) % 12;\n const next = (idx + 1) % 12;\n if (type === 'major') {\n return [\n { numeral: 'I', chord: KEYS[idx].major, chordType: copy.major, role: copy.roles.tonic, state: 'tonic' },\n { numeral: 'ii', chord: KEYS[prev].minor, chordType: copy.minor, role: copy.roles.supertonic, state: 'scale-minor' },\n { numeral: 'iii', chord: KEYS[next].minor, chordType: copy.minor, role: copy.roles.mediant, state: 'scale-minor' },\n { numeral: 'IV', chord: KEYS[prev].major, chordType: copy.major, role: copy.roles.subdominant, state: 'scale-major' },\n { numeral: 'V', chord: KEYS[next].major, chordType: copy.major, role: copy.roles.dominant, state: 'scale-major' },\n { numeral: 'vi', chord: KEYS[idx].minor, chordType: copy.minor, role: copy.roles.submediant, state: 'scale-minor' },\n { numeral: 'vii°', chord: dimChord(idx), chordType: copy.diminished, role: copy.roles.leadingTone, state: 'scale-diminished' },\n ];\n } else {\n return [\n { numeral: 'i', chord: KEYS[idx].minor, chordType: copy.minor, role: copy.roles.tonic, state: 'tonic' },\n { numeral: 'iv', chord: KEYS[prev].minor, chordType: copy.minor, role: copy.roles.subdominant, state: 'scale-minor' },\n { numeral: 'v', chord: KEYS[next].minor, chordType: copy.minor, role: copy.roles.dominant, state: 'scale-minor' },\n { numeral: 'III', chord: KEYS[idx].major, chordType: copy.major, role: copy.roles.mediant, state: 'scale-major' },\n { numeral: 'VI', chord: KEYS[prev].major, chordType: copy.major, role: copy.roles.submediant, state: 'scale-major' },\n { numeral: 'VII', chord: KEYS[next].major, chordType: copy.major, role: copy.roles.subtonic, state: 'scale-major' },\n { numeral: 'ii°', chord: dimChord(idx), chordType: copy.diminished, role: copy.roles.supertonic, state: 'scale-diminished' },\n ];\n }\n}\n\n@Component({\n selector: 'chord-section',\n standalone: true,\n imports: [NgClass],\n templateUrl: './chord-section.component.html',\n styleUrl: './chord-section.component.scss',\n})\nexport class ChordSectionComponent {\n readonly chords = input<string[]>([]);\n readonly language = input<Language>('en');\n readonly text = computed(() => COPY[this.language()]);\n\n readonly match = computed(() => {\n const input = this.chords();\n if (!input.length) return null;\n const inputSet = new Set(input);\n let best: { idx: number; type: 'major' | 'minor'; score: number } | null = null;\n for (let idx = 0; idx < 12; idx++) {\n for (const type of ['major', 'minor'] as const) {\n // chord names are language-independent; COPY.en is a dummy for label fields\n const score = buildChordRows(idx, type, COPY.en).filter((r) => inputSet.has(r.chord)).length;\n if (!best || score > best.score) best = { idx, type, score };\n }\n }\n return best && best.score > 0 ? best : null;\n });\n\n readonly rows = computed(() => {\n const m = this.match();\n if (!m) return [];\n return buildChordRows(m.idx, m.type, this.text());\n });\n\n readonly info = computed(() => {\n const m = this.match();\n if (!m) return null;\n const copy = this.text();\n const key = KEYS[m.idx];\n return {\n fullName: `${m.type === 'major' ? key.major : key.minor} ${m.type === 'major' ? copy.major : copy.minor}`,\n relativeKey: m.type === 'major'\n ? `${copy.relativeMinor}: ${key.minor}`\n : `${copy.relativeMajor}: ${key.major}`,\n };\n });\n}\n\n@Component({\n selector: 'the-chords-circle-of-fifths',\n standalone: true,\n imports: [NgClass, ChordSectionComponent],\n templateUrl: './circle-of-fifths.html',\n styleUrl: './circle-of-fifths.scss',\n host: { '[attr.lang]': 'language()' },\n})\nexport class CircleOfFifthsComponent {\n readonly language = input<Language>('en');\n readonly text = computed(() => COPY[this.language()]);\n readonly CX = 300;\n readonly CY = 300;\n\n readonly KEY_SIG = { inner: 250, outer: 288 };\n readonly MAJOR = { inner: 172, outer: 250 };\n readonly MINOR = { inner: 102, outer: 172 };\n\n readonly keys = KEYS;\n\n selectedIndex = signal<number | null>(null);\n selectedType = signal<'major' | 'minor' | null>(null);\n readonly activeProgressionDefs = signal<ProgressionDefinition[] | null>(null);\n readonly expandedCards = signal<Set<string>>(new Set());\n\n private get slice() {\n const idx = this.selectedIndex();\n if (idx === null) return null;\n return {\n prev: (idx - 1 + 12) % 12,\n curr: idx,\n next: (idx + 1) % 12,\n };\n }\n\n selectKey(index: number, type: 'major' | 'minor'): void {\n if (this.selectedIndex() === index && this.selectedType() === type) {\n this.selectedIndex.set(null);\n this.selectedType.set(null);\n } else {\n this.selectedIndex.set(index);\n this.selectedType.set(type);\n }\n this.activeProgressionDefs.set(null);\n this.expandedCards.set(new Set());\n }\n\n randomizeProgressions(): void {\n const type = this.selectedType();\n if (!type) return;\n // ponytail: sort-shuffle is fine for UI randomness\n this.activeProgressionDefs.set([...PROGRESSIONS[type]].sort(() => Math.random() - 0.5).slice(0, 4));\n this.expandedCards.set(new Set());\n }\n\n toggleCard(name: string): void {\n this.expandedCards.update((set) => {\n const next = new Set(set);\n next.has(name) ? next.delete(name) : next.add(name);\n return next;\n });\n }\n\n copyChords(prog: { name: string; chords: string[]; sections: { label: string; chords: string[] }[] }): void {\n const text = this.expandedCards().has(prog.name)\n ? [`${this.text().verse}: ${prog.chords.join(', ')}`, ...prog.sections.map((s) => `${s.label}: ${s.chords.join(', ')}`)].join('; ')\n : prog.chords.join(', ');\n navigator.clipboard.writeText(text);\n }\n\n getMajorState(index: number): string {\n const s = this.slice;\n if (!s) return '';\n const type = this.selectedType();\n if (index === s.curr) return type === 'major' ? 'tonic' : 'scale-major';\n if (index === s.prev || index === s.next) return 'scale-major';\n return '';\n }\n\n getMinorState(index: number): string {\n const s = this.slice;\n if (!s) return '';\n const type = this.selectedType();\n const dimIdx = (s.curr + 2) % 12;\n if (index === s.curr) return type === 'minor' ? 'tonic' : 'scale-minor';\n if (index === s.prev || index === s.next) return 'scale-minor';\n if (index === dimIdx) return 'scale-diminished';\n return '';\n }\n\n getMajorRole(index: number): string {\n const s = this.slice;\n if (!s) return '';\n const type = this.selectedType();\n if (type === 'major') {\n if (index === s.curr) return 'I';\n if (index === s.prev) return 'IV';\n if (index === s.next) return 'V';\n } else {\n if (index === s.curr) return 'III';\n if (index === s.prev) return 'VI';\n if (index === s.next) return 'VII';\n }\n return '';\n }\n\n getMinorRole(index: number): string {\n const s = this.slice;\n if (!s) return '';\n const type = this.selectedType();\n const dimIdx = (s.curr + 2) % 12;\n if (type === 'major') {\n if (index === s.curr) return 'vi';\n if (index === s.prev) return 'ii';\n if (index === s.next) return 'iii';\n if (index === dimIdx) return 'vii°';\n } else {\n if (index === s.curr) return 'i';\n if (index === s.prev) return 'iv';\n if (index === s.next) return 'v';\n if (index === dimIdx) return 'ii°';\n }\n return '';\n }\n\n arcPath(innerR: number, outerR: number, index: number, gap = 1.5): string {\n const mid = index * 30 - 90;\n const s = (mid - 15 + gap) * (Math.PI / 180);\n const e = (mid + 15 - gap) * (Math.PI / 180);\n const cx = this.CX,\n cy = this.CY;\n const x1 = cx + outerR * Math.cos(s),\n y1 = cy + outerR * Math.sin(s);\n const x2 = cx + outerR * Math.cos(e),\n y2 = cy + outerR * Math.sin(e);\n const x3 = cx + innerR * Math.cos(e),\n y3 = cy + innerR * Math.sin(e);\n const x4 = cx + innerR * Math.cos(s),\n y4 = cy + innerR * Math.sin(s);\n const f = (n: number) => n.toFixed(2);\n return `M${f(x1)} ${f(y1)} A${outerR} ${outerR} 0 0 1 ${f(x2)} ${f(y2)} L${f(x3)} ${f(y3)} A${innerR} ${innerR} 0 0 0 ${f(x4)} ${f(y4)}Z`;\n }\n\n textPos(midR: number, index: number): { x: number; y: number } {\n const a = (index * 30 - 90) * (Math.PI / 180);\n return {\n x: this.CX + midR * Math.cos(a),\n y: this.CY + midR * Math.sin(a),\n };\n }\n\n\n get chordTable(): ChordRow[] {\n const idx = this.selectedIndex();\n const type = this.selectedType();\n if (idx === null || type === null) return [];\n return buildChordRows(idx, type, this.text());\n }\n\n get chordNames(): string[] {\n return this.chordTable.map((r) => r.chord);\n }\n\n getAccidentalText(key: CircleKey): string {\n if (key.sharps > 0) return `${key.sharps}♯`;\n if (key.flats > 0) return `${key.flats}♭`;\n return '—';\n }\n\n get selectedInfo() {\n const idx = this.selectedIndex();\n const type = this.selectedType();\n if (idx === null || type === null) return null;\n const key = KEYS[idx];\n const copy = this.text();\n const scale = type === 'major' ? copy.major : copy.minor;\n return {\n name: type === 'major' ? key.major : key.minor,\n scale,\n fullName: `${type === 'major' ? key.major : key.minor} ${scale}`,\n relativeKey:\n type === 'major'\n ? `${copy.relativeMinor}: ${key.minor}`\n : `${copy.relativeMajor}: ${key.major}`,\n };\n }\n\n private deriveSections(numerals: string[], lookup: Map<string, string>): Section[] {\n const type = this.selectedType()!;\n const copy = this.text();\n const resolve = (ns: string[]) => ({ numerals: ns, chords: ns.map((n) => lookup.get(n) ?? n) });\n\n // Chorus: rotate to lift chord (IV for major, VI for minor); else fixed fallback\n const chorusPivot = type === 'major' ? 'IV' : 'VI';\n const chorusIdx = numerals.indexOf(chorusPivot);\n const chorus = chorusIdx > 0\n ? resolve([...numerals.slice(chorusIdx), ...numerals.slice(0, chorusIdx)])\n : resolve(type === 'major' ? ['I', 'IV', 'V', 'I'] : ['i', 'VI', 'III', 'VII']);\n\n // Bridge: rotate to contrast chord (vi for major, III for minor); else fixed fallback\n const bridgePivot = type === 'major' ? 'vi' : 'III';\n const bridgeIdx = numerals.indexOf(bridgePivot);\n const bridge = bridgeIdx >= 0\n ? resolve([...numerals.slice(bridgeIdx), ...numerals.slice(0, bridgeIdx)])\n : resolve(type === 'major' ? ['vi', 'IV', 'ii', 'V'] : ['III', 'VII', 'VI', 'iv']);\n\n // Outro: fixed resolving pattern per mode\n const outro = resolve(type === 'major' ? ['I', 'V', 'IV', 'I'] : ['i', 'VII', 'VI', 'i']);\n\n return [\n { label: copy.chorus, ...chorus },\n { label: copy.bridge, ...bridge },\n { label: copy.outro, ...outro },\n ];\n }\n\n get progressions(): Progression[] {\n const table = this.chordTable;\n if (!table.length) return [];\n const lookup = new Map(table.map((r) => [r.numeral, r.chord]));\n const language = this.language();\n const type = this.selectedType() === 'major' ? 'major' : 'minor';\n const defs = this.activeProgressionDefs() ?? PROGRESSIONS[type].slice(0, 4);\n return defs.map((definition) => ({\n name: definition.name[language],\n mood: definition.mood[language],\n genre: definition.genre[language],\n numerals: definition.numerals,\n chords: definition.numerals.map((numeral) => lookup.get(numeral) ?? numeral),\n sections: this.deriveSections(definition.numerals, lookup),\n }));\n }\n}\n","@if (rows().length) {\n <section class=\"chord-section\">\n @if (info(); as info) {\n <h2>{{ info.fullName }} — {{ text().diatonicChords }}</h2>\n <p class=\"relative-label\">{{ info.relativeKey }}</p>\n }\n <div class=\"chord-grid\">\n @for (row of rows(); track row.numeral) {\n <div class=\"chord-card\" [ngClass]=\"row.state\">\n <div class=\"chord-numeral\">{{ row.numeral }}</div>\n <div class=\"chord-name\">{{ row.chord }}</div>\n <div class=\"chord-type\">{{ row.chordType }}</div>\n <div class=\"chord-role\">{{ row.role }}</div>\n </div>\n }\n </div>\n </section>\n}\n","<div class=\"page\">\n <header class=\"page-header\">\n <h1>{{ text().title }}</h1>\n <p>{{ text().subtitle }}</p>\n </header>\n\n <div class=\"circle-wrapper\">\n <svg\n viewBox=\"0 0 600 600\"\n xmlns=\"http://www.w3.org/2000/svg\"\n [attr.aria-label]=\"text().ariaLabel\"\n >\n <!-- ── Key Signature Ring ── -->\n @for (key of keys; track key.index) {\n <path [attr.d]=\"arcPath(KEY_SIG.inner, KEY_SIG.outer, key.index)\" class=\"keysig-segment\" />\n <text\n [attr.x]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).y\"\n class=\"keysig-label\"\n >\n {{ getAccidentalText(key) }}\n </text>\n }\n\n <!-- ── Major Keys Ring ── -->\n @for (key of keys; track key.index) {\n <g\n [ngClass]=\"['major-group', getMajorState(key.index)]\"\n (click)=\"selectKey(key.index, 'major')\"\n >\n <path [attr.d]=\"arcPath(MAJOR.inner, MAJOR.outer, key.index)\" />\n\n <text\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\n [attr.y]=\"\n textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y +\n (getMajorRole(key.index) ? -9 : 0)\n \"\n class=\"major-label\"\n >\n {{ key.major }}\n </text>\n\n @if (getMajorRole(key.index)) {\n <text\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y + 11\"\n class=\"role-label\"\n >\n {{ getMajorRole(key.index) }}\n </text>\n }\n </g>\n }\n\n <!-- ── Minor Keys Ring ── -->\n @for (key of keys; track key.index) {\n <g\n [ngClass]=\"['minor-group', getMinorState(key.index)]\"\n (click)=\"selectKey(key.index, 'minor')\"\n >\n <path [attr.d]=\"arcPath(MINOR.inner, MINOR.outer, key.index)\" />\n\n <text\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\n [attr.y]=\"\n textPos((MINOR.inner + MINOR.outer) / 2, key.index).y +\n (getMinorRole(key.index) ? -8 : 0)\n \"\n class=\"minor-label\"\n >\n {{ key.minor }}\n </text>\n\n @if (getMinorRole(key.index)) {\n <text\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\n [attr.y]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).y + 9\"\n class=\"role-label\"\n >\n {{ getMinorRole(key.index) }}\n </text>\n }\n </g>\n }\n\n <!-- ── Outer border ── -->\n <circle cx=\"300\" cy=\"300\" r=\"289\" class=\"outer-border\" />\n\n <!-- ── Center Circle ── -->\n <circle cx=\"300\" cy=\"300\" r=\"99\" class=\"center-circle\" />\n\n @if (selectedInfo) {\n <text x=\"300\" y=\"283\" class=\"center-name\">{{ selectedInfo.name }}</text>\n <text x=\"300\" y=\"304\" class=\"center-scale\">{{ selectedInfo.scale }}</text>\n <text x=\"300\" y=\"322\" class=\"center-relative\">{{ selectedInfo.relativeKey }}</text>\n <text x=\"300\" y=\"338\" class=\"center-deselect\">{{ text().deselect }}</text>\n } @else {\n <text x=\"300\" y=\"293\" class=\"center-hint\">{{ text().clickKey }}</text>\n <text x=\"300\" y=\"312\" class=\"center-hint-sm\">{{ text().seeChords }}</text>\n }\n </svg>\n </div>\n\n <!-- ── Legend ── -->\n <div class=\"legend\">\n <span class=\"legend-item tonic\">{{ text().tonicLegend }}</span>\n <span class=\"legend-item scale-major\">{{ text().majorLegend }}</span>\n <span class=\"legend-item scale-minor\">{{ text().minorLegend }}</span>\n <span class=\"legend-item scale-diminished\">{{ text().diminishedLegend }}</span>\n </div>\n\n <chord-section [chords]=\"chordNames\" [language]=\"language()\"></chord-section>\n\n <!-- ── Chord Progressions ── -->\n @if (progressions.length) {\n <section class=\"progressions-section\">\n <div class=\"progressions-header\">\n <h2>{{ text().commonProgressions }} {{ selectedInfo?.fullName }}</h2>\n <button class=\"btn-randomize\" (click)=\"randomizeProgressions()\">{{ text().generateProgressions }}</button>\n </div>\n <div class=\"progression-list\">\n @for (prog of progressions; track prog.name) {\n <div class=\"progression-card\">\n <div class=\"prog-header\">\n <span class=\"prog-mood\">{{ prog.mood }}</span>\n <span class=\"prog-name\">{{ prog.name }}</span>\n <span class=\"prog-genre\">{{ prog.genre }}</span>\n </div>\n <div class=\"prog-chords\">\n @for (chord of prog.chords; track $index; let last = $last) {\n <span class=\"prog-chord\">{{ chord }}</span>\n @if (!last) {\n <span class=\"prog-arrow\">→</span>\n }\n }\n </div>\n <div class=\"prog-numerals\">\n @for (num of prog.numerals; track $index; let last = $last) {\n <span>{{ num }}</span>\n @if (!last) {\n <span class=\"prog-arrow\">→</span>\n }\n }\n </div>\n <div class=\"prog-expand-row\">\n <button class=\"btn-expand\" (click)=\"toggleCard(prog.name)\">\n {{ expandedCards().has(prog.name) ? text().collapse : text().expand }}\n </button>\n <button class=\"btn-expand\" (click)=\"copyChords(prog)\">\n {{ text().copy }}\n </button>\n </div>\n @if (expandedCards().has(prog.name)) {\n <div class=\"prog-sections\">\n @for (section of prog.sections; track section.label) {\n <div class=\"prog-section\">\n <div class=\"prog-section-label\">{{ section.label }}</div>\n <div class=\"prog-chords\">\n @for (chord of section.chords; track $index; let last = $last) {\n <span class=\"prog-chord\">{{ chord }}</span>\n @if (!last) { <span class=\"prog-arrow\">→</span> }\n }\n </div>\n <div class=\"prog-numerals\">\n @for (num of section.numerals; track $index; let last = $last) {\n <span>{{ num }}</span>\n @if (!last) { <span class=\"prog-arrow\">→</span> }\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </section>\n }\n</div>\n","/*\n * Public API Surface of circle-of-fifths-lib\n */\n\nexport * from './lib/circle-of-fifths';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@gblp/circle-of-fifths",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/elparaquecosadeque/circle-of-fifths"
10
+ },
7
11
  "peerDependencies": {
8
12
  "@angular/common": "^22.0.0",
9
13
  "@angular/core": "^22.0.0"
@@ -14,14 +14,116 @@ interface ChordRow {
14
14
  role: string;
15
15
  state: 'tonic' | 'scale-major' | 'scale-minor' | 'scale-diminished';
16
16
  }
17
+ interface Section {
18
+ label: string;
19
+ numerals: string[];
20
+ chords: string[];
21
+ }
17
22
  interface Progression {
18
23
  name: string;
19
24
  mood: string;
20
25
  genre: string;
21
26
  numerals: string[];
22
27
  chords: string[];
28
+ sections: Section[];
23
29
  }
24
30
  type Language = 'en' | 'es';
31
+ type LocalizedText = Record<Language, string>;
32
+ interface ProgressionDefinition {
33
+ name: LocalizedText;
34
+ mood: LocalizedText;
35
+ genre: LocalizedText;
36
+ numerals: string[];
37
+ }
38
+ declare class ChordSectionComponent {
39
+ readonly chords: _angular_core.InputSignal<string[]>;
40
+ readonly language: _angular_core.InputSignal<Language>;
41
+ readonly text: _angular_core.Signal<{
42
+ readonly title: "Circle of Fifths";
43
+ readonly subtitle: "Click any major or minor key to highlight its diatonic chords";
44
+ readonly ariaLabel: "Circle of Fifths";
45
+ readonly clickKey: "Click a key";
46
+ readonly seeChords: "to see its chords";
47
+ readonly deselect: "↩ click to deselect";
48
+ readonly tonicLegend: "Tonic (I / i)";
49
+ readonly majorLegend: "Major chords (IV, V)";
50
+ readonly minorLegend: "Minor chords (ii, iii, vi)";
51
+ readonly diminishedLegend: "Diminished (vii° / ii°)";
52
+ readonly diatonicChords: "Diatonic Chords";
53
+ readonly commonProgressions: "Common Progressions in";
54
+ readonly generateProgressions: "Shuffle";
55
+ readonly chorus: "Chorus";
56
+ readonly bridge: "Bridge";
57
+ readonly outro: "Outro";
58
+ readonly verse: "Verse";
59
+ readonly copy: "Copy";
60
+ readonly expand: "Expand";
61
+ readonly collapse: "Collapse";
62
+ readonly major: "Major";
63
+ readonly minor: "Minor";
64
+ readonly diminished: "diminished";
65
+ readonly relativeMinor: "Relative minor";
66
+ readonly relativeMajor: "Relative major";
67
+ readonly roles: {
68
+ readonly tonic: "Tonic";
69
+ readonly supertonic: "Supertonic";
70
+ readonly mediant: "Mediant";
71
+ readonly subdominant: "Subdominant";
72
+ readonly dominant: "Dominant";
73
+ readonly submediant: "Submediant";
74
+ readonly leadingTone: "Leading Tone";
75
+ readonly subtonic: "Subtonic";
76
+ };
77
+ } | {
78
+ readonly title: "Círculo de quintas";
79
+ readonly subtitle: "Haz clic en una tonalidad mayor o menor para resaltar sus acordes diatónicos";
80
+ readonly ariaLabel: "Círculo de quintas";
81
+ readonly clickKey: "Haz clic en una tonalidad";
82
+ readonly seeChords: "para ver sus acordes";
83
+ readonly deselect: "↩ haz clic para deseleccionar";
84
+ readonly tonicLegend: "Tónica (I / i)";
85
+ readonly majorLegend: "Acordes mayores (IV, V)";
86
+ readonly minorLegend: "Acordes menores (ii, iii, vi)";
87
+ readonly diminishedLegend: "Disminuido (vii° / ii°)";
88
+ readonly diatonicChords: "Acordes diatónicos";
89
+ readonly commonProgressions: "Progresiones comunes en";
90
+ readonly generateProgressions: "Generar nuevos";
91
+ readonly chorus: "Coro";
92
+ readonly bridge: "Puente";
93
+ readonly outro: "Final";
94
+ readonly verse: "Estrofa";
95
+ readonly copy: "Copiar";
96
+ readonly expand: "Expandir";
97
+ readonly collapse: "Colapsar";
98
+ readonly major: "Mayor";
99
+ readonly minor: "Menor";
100
+ readonly diminished: "disminuido";
101
+ readonly relativeMinor: "Relativa menor";
102
+ readonly relativeMajor: "Relativa mayor";
103
+ readonly roles: {
104
+ readonly tonic: "Tónica";
105
+ readonly supertonic: "Supertónica";
106
+ readonly mediant: "Mediante";
107
+ readonly subdominant: "Subdominante";
108
+ readonly dominant: "Dominante";
109
+ readonly submediant: "Submediante";
110
+ readonly leadingTone: "Sensible";
111
+ readonly subtonic: "Subtónica";
112
+ };
113
+ }>;
114
+ readonly match: _angular_core.Signal<{
115
+ idx: number;
116
+ type: "major" | "minor";
117
+ score: number;
118
+ } | null>;
119
+ readonly rows: _angular_core.Signal<ChordRow[]>;
120
+ readonly info: _angular_core.Signal<{
121
+ fullName: string;
122
+ relativeKey: string;
123
+ } | null>;
124
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ChordSectionComponent, never>;
125
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ChordSectionComponent, "chord-section", never, { "chords": { "alias": "chords"; "required": false; "isSignal": true; }; "language": { "alias": "language"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
126
+ }
25
127
  declare class CircleOfFifthsComponent {
26
128
  readonly language: _angular_core.InputSignal<Language>;
27
129
  readonly text: _angular_core.Signal<{
@@ -37,6 +139,14 @@ declare class CircleOfFifthsComponent {
37
139
  readonly diminishedLegend: "Diminished (vii° / ii°)";
38
140
  readonly diatonicChords: "Diatonic Chords";
39
141
  readonly commonProgressions: "Common Progressions in";
142
+ readonly generateProgressions: "Shuffle";
143
+ readonly chorus: "Chorus";
144
+ readonly bridge: "Bridge";
145
+ readonly outro: "Outro";
146
+ readonly verse: "Verse";
147
+ readonly copy: "Copy";
148
+ readonly expand: "Expand";
149
+ readonly collapse: "Collapse";
40
150
  readonly major: "Major";
41
151
  readonly minor: "Minor";
42
152
  readonly diminished: "diminished";
@@ -65,6 +175,14 @@ declare class CircleOfFifthsComponent {
65
175
  readonly diminishedLegend: "Disminuido (vii° / ii°)";
66
176
  readonly diatonicChords: "Acordes diatónicos";
67
177
  readonly commonProgressions: "Progresiones comunes en";
178
+ readonly generateProgressions: "Generar nuevos";
179
+ readonly chorus: "Coro";
180
+ readonly bridge: "Puente";
181
+ readonly outro: "Final";
182
+ readonly verse: "Estrofa";
183
+ readonly copy: "Copiar";
184
+ readonly expand: "Expandir";
185
+ readonly collapse: "Colapsar";
68
186
  readonly major: "Mayor";
69
187
  readonly minor: "Menor";
70
188
  readonly diminished: "disminuido";
@@ -98,8 +216,20 @@ declare class CircleOfFifthsComponent {
98
216
  readonly keys: CircleKey[];
99
217
  selectedIndex: _angular_core.WritableSignal<number | null>;
100
218
  selectedType: _angular_core.WritableSignal<"major" | "minor" | null>;
219
+ readonly activeProgressionDefs: _angular_core.WritableSignal<ProgressionDefinition[] | null>;
220
+ readonly expandedCards: _angular_core.WritableSignal<Set<string>>;
101
221
  private get slice();
102
222
  selectKey(index: number, type: 'major' | 'minor'): void;
223
+ randomizeProgressions(): void;
224
+ toggleCard(name: string): void;
225
+ copyChords(prog: {
226
+ name: string;
227
+ chords: string[];
228
+ sections: {
229
+ label: string;
230
+ chords: string[];
231
+ }[];
232
+ }): void;
103
233
  getMajorState(index: number): string;
104
234
  getMinorState(index: number): string;
105
235
  getMajorRole(index: number): string;
@@ -109,7 +239,8 @@ declare class CircleOfFifthsComponent {
109
239
  x: number;
110
240
  y: number;
111
241
  };
112
- private dimChord;
242
+ get chordTable(): ChordRow[];
243
+ get chordNames(): string[];
113
244
  getAccidentalText(key: CircleKey): string;
114
245
  get selectedInfo(): {
115
246
  name: string;
@@ -117,10 +248,10 @@ declare class CircleOfFifthsComponent {
117
248
  fullName: string;
118
249
  relativeKey: string;
119
250
  } | null;
120
- get chordTable(): ChordRow[];
251
+ private deriveSections;
121
252
  get progressions(): Progression[];
122
253
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<CircleOfFifthsComponent, never>;
123
254
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<CircleOfFifthsComponent, "the-chords-circle-of-fifths", never, { "language": { "alias": "language"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
124
255
  }
125
256
 
126
- export { CircleOfFifthsComponent };
257
+ export { ChordSectionComponent, CircleOfFifthsComponent };