@gblp/circle-of-fifths 0.3.0 → 0.3.2
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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, computed, Component, signal } from '@angular/core';
|
|
2
|
+
import { input, computed, Component, signal, effect } from '@angular/core';
|
|
3
3
|
import { NgClass } from '@angular/common';
|
|
4
4
|
|
|
5
5
|
const PROGRESSIONS = {
|
|
@@ -126,6 +126,8 @@ const COPY = {
|
|
|
126
126
|
copy: 'Copy',
|
|
127
127
|
expand: 'Expand',
|
|
128
128
|
collapse: 'Collapse',
|
|
129
|
+
transposeLabel: 'Transpose to:',
|
|
130
|
+
spiceLabels: ['🌶 Spice', '🌶🌶 Spicier', '🔥 Hot!', '↩ Plain'],
|
|
129
131
|
major: 'Major',
|
|
130
132
|
minor: 'Minor',
|
|
131
133
|
diminished: 'diminished',
|
|
@@ -163,6 +165,8 @@ const COPY = {
|
|
|
163
165
|
copy: 'Copiar',
|
|
164
166
|
expand: 'Expandir',
|
|
165
167
|
collapse: 'Colapsar',
|
|
168
|
+
transposeLabel: 'Transponer a:',
|
|
169
|
+
spiceLabels: ['🌶 Condimentar', '🌶🌶 Más sabor', '🔥 ¡Picante!', '↩ Simple'],
|
|
166
170
|
major: 'Mayor',
|
|
167
171
|
minor: 'Menor',
|
|
168
172
|
diminished: 'disminuido',
|
|
@@ -224,6 +228,35 @@ function buildChordRows(idx, type, copy) {
|
|
|
224
228
|
];
|
|
225
229
|
}
|
|
226
230
|
}
|
|
231
|
+
// Suffix to append per numeral at spice levels 1, 2, 3.
|
|
232
|
+
// Minor chords already carry 'm' (e.g. "Dm"), so suffix starts from the number.
|
|
233
|
+
// Diminished chords carry '°' which gets replaced entirely.
|
|
234
|
+
const SPICE_SUFFIXES = {
|
|
235
|
+
'I': ['maj7', 'maj9', 'maj9'],
|
|
236
|
+
'ii': ['7', '9', '11'],
|
|
237
|
+
'iii': ['7', '7', '7'],
|
|
238
|
+
'IV': ['maj7', 'maj9', 'maj7♯11'],
|
|
239
|
+
'V': ['7', '9', '13'],
|
|
240
|
+
'vi': ['7', '9', '11'],
|
|
241
|
+
'vii°': ['m7♭5', 'm7♭5', 'm7♭5'],
|
|
242
|
+
'i': ['7', '9', '9'],
|
|
243
|
+
'iv': ['7', '9', '11'],
|
|
244
|
+
'v': ['7', '7', '7'],
|
|
245
|
+
'III': ['maj7', 'maj7', 'maj7'],
|
|
246
|
+
'VI': ['maj7', 'maj9', 'maj9'],
|
|
247
|
+
'VII': ['7', '9', '13'],
|
|
248
|
+
'ii°': ['m7♭5', 'm7♭5', 'm7♭5'],
|
|
249
|
+
};
|
|
250
|
+
function spiceChord(chord, numeral, level) {
|
|
251
|
+
if (level === 0)
|
|
252
|
+
return chord;
|
|
253
|
+
const suffixes = SPICE_SUFFIXES[numeral];
|
|
254
|
+
if (!suffixes)
|
|
255
|
+
return chord;
|
|
256
|
+
const suffix = suffixes[level - 1];
|
|
257
|
+
// Diminished chords: strip '°', then the suffix already starts with 'm'
|
|
258
|
+
return chord.endsWith('°') ? chord.slice(0, -1) + suffix : chord + suffix;
|
|
259
|
+
}
|
|
227
260
|
class ChordSectionComponent {
|
|
228
261
|
chords = input([], /* @ts-ignore */
|
|
229
262
|
...(ngDevMode ? [{ debugName: "chords" }] : /* istanbul ignore next */ []));
|
|
@@ -270,11 +303,11 @@ class ChordSectionComponent {
|
|
|
270
303
|
}, /* @ts-ignore */
|
|
271
304
|
...(ngDevMode ? [{ debugName: "info" }] : /* istanbul ignore next */ []));
|
|
272
305
|
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) {\
|
|
306
|
+
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
307
|
}
|
|
275
308
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ChordSectionComponent, decorators: [{
|
|
276
309
|
type: Component,
|
|
277
|
-
args: [{ selector: 'chord-section', standalone: true, imports: [NgClass], template: "@if (rows().length) {\
|
|
310
|
+
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
311
|
}], propDecorators: { chords: [{ type: i0.Input, args: [{ isSignal: true, alias: "chords", required: false }] }], language: [{ type: i0.Input, args: [{ isSignal: true, alias: "language", required: false }] }] } });
|
|
279
312
|
class CircleOfFifthsComponent {
|
|
280
313
|
language = input('en', /* @ts-ignore */
|
|
@@ -295,6 +328,26 @@ class CircleOfFifthsComponent {
|
|
|
295
328
|
...(ngDevMode ? [{ debugName: "activeProgressionDefs" }] : /* istanbul ignore next */ []));
|
|
296
329
|
expandedCards = signal(new Set(), /* @ts-ignore */
|
|
297
330
|
...(ngDevMode ? [{ debugName: "expandedCards" }] : /* istanbul ignore next */ []));
|
|
331
|
+
transposeMap = signal(new Map(), /* @ts-ignore */
|
|
332
|
+
...(ngDevMode ? [{ debugName: "transposeMap" }] : /* istanbul ignore next */ []));
|
|
333
|
+
spiceMap = signal(new Map(), /* @ts-ignore */
|
|
334
|
+
...(ngDevMode ? [{ debugName: "spiceMap" }] : /* istanbul ignore next */ []));
|
|
335
|
+
constructor() {
|
|
336
|
+
// Restore selected key from URL hash (format: #{index}-{type})
|
|
337
|
+
const [idxStr, type] = location.hash.slice(1).split('-');
|
|
338
|
+
const idx = Number(idxStr);
|
|
339
|
+
if (!isNaN(idx) && idx >= 0 && idx < 12 && (type === 'major' || type === 'minor')) {
|
|
340
|
+
this.selectedIndex.set(idx);
|
|
341
|
+
this.selectedType.set(type);
|
|
342
|
+
}
|
|
343
|
+
// Keep URL hash in sync with selected key
|
|
344
|
+
effect(() => {
|
|
345
|
+
const idx = this.selectedIndex();
|
|
346
|
+
const type = this.selectedType();
|
|
347
|
+
const hash = idx !== null && type !== null ? `#${idx}-${type}` : '';
|
|
348
|
+
history.replaceState(null, '', location.pathname + location.search + hash);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
298
351
|
get slice() {
|
|
299
352
|
const idx = this.selectedIndex();
|
|
300
353
|
if (idx === null)
|
|
@@ -316,6 +369,8 @@ class CircleOfFifthsComponent {
|
|
|
316
369
|
}
|
|
317
370
|
this.activeProgressionDefs.set(null);
|
|
318
371
|
this.expandedCards.set(new Set());
|
|
372
|
+
this.transposeMap.set(new Map());
|
|
373
|
+
this.spiceMap.set(new Map());
|
|
319
374
|
}
|
|
320
375
|
randomizeProgressions() {
|
|
321
376
|
const type = this.selectedType();
|
|
@@ -324,6 +379,8 @@ class CircleOfFifthsComponent {
|
|
|
324
379
|
// ponytail: sort-shuffle is fine for UI randomness
|
|
325
380
|
this.activeProgressionDefs.set([...PROGRESSIONS[type]].sort(() => Math.random() - 0.5).slice(0, 4));
|
|
326
381
|
this.expandedCards.set(new Set());
|
|
382
|
+
this.transposeMap.set(new Map());
|
|
383
|
+
this.spiceMap.set(new Map());
|
|
327
384
|
}
|
|
328
385
|
toggleCard(name) {
|
|
329
386
|
this.expandedCards.update((set) => {
|
|
@@ -338,6 +395,27 @@ class CircleOfFifthsComponent {
|
|
|
338
395
|
: prog.chords.join(', ');
|
|
339
396
|
navigator.clipboard.writeText(text);
|
|
340
397
|
}
|
|
398
|
+
setTranspose(nameEn, targetIdx) {
|
|
399
|
+
this.transposeMap.update((m) => {
|
|
400
|
+
const next = new Map(m);
|
|
401
|
+
targetIdx === this.selectedIndex() ? next.delete(nameEn) : next.set(nameEn, targetIdx);
|
|
402
|
+
return next;
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
cycleSpice(nameEn) {
|
|
406
|
+
this.spiceMap.update((m) => {
|
|
407
|
+
const next = new Map(m);
|
|
408
|
+
const nextLevel = ((next.get(nameEn) ?? 0) + 1) % 4;
|
|
409
|
+
nextLevel === 0 ? next.delete(nameEn) : next.set(nameEn, nextLevel);
|
|
410
|
+
return next;
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
get transposeKeys() {
|
|
414
|
+
const type = this.selectedType();
|
|
415
|
+
if (!type)
|
|
416
|
+
return [];
|
|
417
|
+
return KEYS.map((k) => ({ idx: k.index, name: type === 'major' ? k.major : k.minor }));
|
|
418
|
+
}
|
|
341
419
|
getMajorState(index) {
|
|
342
420
|
const s = this.slice;
|
|
343
421
|
if (!s)
|
|
@@ -495,26 +573,37 @@ class CircleOfFifthsComponent {
|
|
|
495
573
|
const table = this.chordTable;
|
|
496
574
|
if (!table.length)
|
|
497
575
|
return [];
|
|
498
|
-
const
|
|
576
|
+
const selIdx = this.selectedIndex();
|
|
577
|
+
const transposeMap = this.transposeMap();
|
|
578
|
+
const spiceMap = this.spiceMap();
|
|
499
579
|
const language = this.language();
|
|
500
580
|
const type = this.selectedType() === 'major' ? 'major' : 'minor';
|
|
501
581
|
const defs = this.activeProgressionDefs() ?? PROGRESSIONS[type].slice(0, 4);
|
|
502
|
-
return defs.map((definition) =>
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
582
|
+
return defs.map((definition) => {
|
|
583
|
+
const targetIdx = transposeMap.get(definition.name.en) ?? selIdx;
|
|
584
|
+
const spiceLevel = spiceMap.get(definition.name.en) ?? 0;
|
|
585
|
+
const baseRows = buildChordRows(targetIdx, type, this.text());
|
|
586
|
+
const lookup = new Map(baseRows.map((r) => [r.numeral, spiceChord(r.chord, r.numeral, spiceLevel)]));
|
|
587
|
+
return {
|
|
588
|
+
nameEn: definition.name.en,
|
|
589
|
+
name: definition.name[language],
|
|
590
|
+
mood: definition.mood[language],
|
|
591
|
+
genre: definition.genre[language],
|
|
592
|
+
numerals: definition.numerals,
|
|
593
|
+
chords: definition.numerals.map((n) => lookup.get(n) ?? n),
|
|
594
|
+
sections: this.deriveSections(definition.numerals, lookup),
|
|
595
|
+
transposeIdx: targetIdx,
|
|
596
|
+
spiceLevel,
|
|
597
|
+
};
|
|
598
|
+
});
|
|
510
599
|
}
|
|
511
600
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CircleOfFifthsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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\">\r\n <header class=\"page-header\">\r\n <h1>{{ text().title }}</h1>\r\n <p>{{ text().subtitle }}</p>\r\n </header>\r\n\r\n <div class=\"circle-wrapper\">\r\n <svg\r\n viewBox=\"0 0 600 600\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n [attr.aria-label]=\"text().ariaLabel\"\r\n >\r\n <!-- \u2500\u2500 Key Signature Ring \u2500\u2500 -->\r\n @for (key of keys; track key.index) {\r\n <path [attr.d]=\"arcPath(KEY_SIG.inner, KEY_SIG.outer, key.index)\" class=\"keysig-segment\" />\r\n <text\r\n [attr.x]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).x\"\r\n [attr.y]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).y\"\r\n class=\"keysig-label\"\r\n >\r\n {{ getAccidentalText(key) }}\r\n </text>\r\n }\r\n\r\n <!-- \u2500\u2500 Major Keys Ring \u2500\u2500 -->\r\n @for (key of keys; track key.index) {\r\n <g\r\n [ngClass]=\"['major-group', getMajorState(key.index)]\"\r\n (click)=\"selectKey(key.index, 'major')\"\r\n >\r\n <path [attr.d]=\"arcPath(MAJOR.inner, MAJOR.outer, key.index)\" />\r\n\r\n <text\r\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\r\n [attr.y]=\"\r\n textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y +\r\n (getMajorRole(key.index) ? -9 : 0)\r\n \"\r\n class=\"major-label\"\r\n >\r\n {{ key.major }}\r\n </text>\r\n\r\n @if (getMajorRole(key.index)) {\r\n <text\r\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\r\n [attr.y]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y + 11\"\r\n class=\"role-label\"\r\n >\r\n {{ getMajorRole(key.index) }}\r\n </text>\r\n }\r\n </g>\r\n }\r\n\r\n <!-- \u2500\u2500 Minor Keys Ring \u2500\u2500 -->\r\n @for (key of keys; track key.index) {\r\n <g\r\n [ngClass]=\"['minor-group', getMinorState(key.index)]\"\r\n (click)=\"selectKey(key.index, 'minor')\"\r\n >\r\n <path [attr.d]=\"arcPath(MINOR.inner, MINOR.outer, key.index)\" />\r\n\r\n <text\r\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\r\n [attr.y]=\"\r\n textPos((MINOR.inner + MINOR.outer) / 2, key.index).y +\r\n (getMinorRole(key.index) ? -8 : 0)\r\n \"\r\n class=\"minor-label\"\r\n >\r\n {{ key.minor }}\r\n </text>\r\n\r\n @if (getMinorRole(key.index)) {\r\n <text\r\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\r\n [attr.y]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).y + 9\"\r\n class=\"role-label\"\r\n >\r\n {{ getMinorRole(key.index) }}\r\n </text>\r\n }\r\n </g>\r\n }\r\n\r\n <!-- \u2500\u2500 Outer border \u2500\u2500 -->\r\n <circle cx=\"300\" cy=\"300\" r=\"289\" class=\"outer-border\" />\r\n\r\n <!-- \u2500\u2500 Center Circle \u2500\u2500 -->\r\n <circle cx=\"300\" cy=\"300\" r=\"99\" class=\"center-circle\" />\r\n\r\n @if (selectedInfo) {\r\n <text x=\"300\" y=\"283\" class=\"center-name\">{{ selectedInfo.name }}</text>\r\n <text x=\"300\" y=\"304\" class=\"center-scale\">{{ selectedInfo.scale }}</text>\r\n <text x=\"300\" y=\"322\" class=\"center-relative\">{{ selectedInfo.relativeKey }}</text>\r\n <text x=\"300\" y=\"338\" class=\"center-deselect\">{{ text().deselect }}</text>\r\n } @else {\r\n <text x=\"300\" y=\"293\" class=\"center-hint\">{{ text().clickKey }}</text>\r\n <text x=\"300\" y=\"312\" class=\"center-hint-sm\">{{ text().seeChords }}</text>\r\n }\r\n </svg>\r\n </div>\r\n\r\n <!-- \u2500\u2500 Legend \u2500\u2500 -->\r\n <div class=\"legend\">\r\n <span class=\"legend-item tonic\">{{ text().tonicLegend }}</span>\r\n <span class=\"legend-item scale-major\">{{ text().majorLegend }}</span>\r\n <span class=\"legend-item scale-minor\">{{ text().minorLegend }}</span>\r\n <span class=\"legend-item scale-diminished\">{{ text().diminishedLegend }}</span>\r\n </div>\r\n\r\n <chord-section [chords]=\"chordNames\" [language]=\"language()\"></chord-section>\r\n\r\n <!-- \u2500\u2500 Chord Progressions \u2500\u2500 -->\r\n @if (progressions.length) {\r\n <section class=\"progressions-section\">\r\n <div class=\"progressions-header\">\r\n <h2>{{ text().commonProgressions }} {{ selectedInfo?.fullName }}</h2>\r\n <button class=\"btn-randomize\" (click)=\"randomizeProgressions()\">{{ text().generateProgressions }}</button>\r\n </div>\r\n <div class=\"progression-list\">\r\n @for (prog of progressions; track prog.name) {\r\n <div class=\"progression-card\">\r\n <div class=\"prog-header\">\r\n <span class=\"prog-mood\">{{ prog.mood }}</span>\r\n <span class=\"prog-name\">{{ prog.name }}</span>\r\n <span class=\"prog-genre\">{{ prog.genre }}</span>\r\n </div>\r\n <div class=\"prog-chords\">\r\n @for (chord of prog.chords; track $index; let last = $last) {\r\n <span class=\"prog-chord\">{{ chord }}</span>\r\n @if (!last) {\r\n <span class=\"prog-arrow\">\u2192</span>\r\n }\r\n }\r\n </div>\r\n <div class=\"prog-numerals\">\r\n @for (num of prog.numerals; track $index; let last = $last) {\r\n <span>{{ num }}</span>\r\n @if (!last) {\r\n <span class=\"prog-arrow\">\u2192</span>\r\n }\r\n }\r\n </div>\r\n <div class=\"prog-expand-row\">\r\n <button class=\"btn-expand\" (click)=\"toggleCard(prog.name)\">\r\n {{ expandedCards().has(prog.name) ? text().collapse : text().expand }}\r\n </button>\r\n <button class=\"btn-expand\" (click)=\"copyChords(prog)\">\r\n {{ text().copy }}\r\n </button>\r\n </div>\r\n @if (expandedCards().has(prog.name)) {\r\n <div class=\"prog-sections\">\r\n @for (section of prog.sections; track section.label) {\r\n <div class=\"prog-section\">\r\n <div class=\"prog-section-label\">{{ section.label }}</div>\r\n <div class=\"prog-chords\">\r\n @for (chord of section.chords; track $index; let last = $last) {\r\n <span class=\"prog-chord\">{{ chord }}</span>\r\n @if (!last) { <span class=\"prog-arrow\">\u2192</span> }\r\n }\r\n </div>\r\n <div class=\"prog-numerals\">\r\n @for (num of section.numerals; track $index; let last = $last) {\r\n <span>{{ num }}</span>\r\n @if (!last) { <span class=\"prog-arrow\">\u2192</span> }\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </section>\r\n }\r\n</div>\r\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"] }] });
|
|
601
|
+
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-transpose-row\">\n <label class=\"prog-transpose-label\">{{ text().transposeLabel }}</label>\n <select class=\"prog-transpose-select\" (change)=\"setTranspose(prog.nameEn, +$any($event.target).value)\">\n @for (key of transposeKeys; track key.idx) {\n <option [value]=\"key.idx\" [selected]=\"key.idx === prog.transposeIdx\">{{ key.name }}</option>\n }\n </select>\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 <button class=\"btn-spice\" [class.btn-spice--active]=\"prog.spiceLevel > 0\" (click)=\"cycleSpice(prog.nameEn)\">\n {{ text().spiceLabels[prog.spiceLevel] }}\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-transpose-row{display:flex;align-items:center;gap:6px;margin-top:8px}.prog-transpose-label{font-size:.68rem;font-weight:600;color:var(--_chords-muted);white-space:nowrap}.prog-transpose-select{font-size:.72rem;font-weight:600;color:var(--_chords-muted);background:transparent;border:1px solid var(--_chords-border);border-radius:8px;padding:2px 6px;cursor:pointer;transition:border-color .15s,color .15s;appearance:auto}.prog-transpose-select:hover,.prog-transpose-select:focus{border-color:var(--_chords-primary);color:var(--_chords-primary);outline:none}.prog-expand-row{display:flex;justify-content:flex-end;margin-top:10px}.btn-spice{background:transparent;border:1px solid var(--cof-accent, #e09030);border-radius:6px;color:var(--cof-accent, #e09030);cursor:pointer;font-size:.75rem;padding:4px 10px;transition:background .2s,color .2s}.btn-spice:hover{background:var(--cof-accent, #e09030);color:#fff}.btn-spice.btn-spice--active{background:var(--cof-accent, #e09030);color:#fff;font-weight:600}.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"] }] });
|
|
513
602
|
}
|
|
514
603
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CircleOfFifthsComponent, decorators: [{
|
|
515
604
|
type: Component,
|
|
516
|
-
args: [{ selector: 'the-chords-circle-of-fifths', standalone: true, imports: [NgClass, ChordSectionComponent], host: { '[attr.lang]': 'language()' }, template: "<div class=\"page\">\r\n <header class=\"page-header\">\r\n <h1>{{ text().title }}</h1>\r\n <p>{{ text().subtitle }}</p>\r\n </header>\r\n\r\n <div class=\"circle-wrapper\">\r\n <svg\r\n viewBox=\"0 0 600 600\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n [attr.aria-label]=\"text().ariaLabel\"\r\n >\r\n <!-- \u2500\u2500 Key Signature Ring \u2500\u2500 -->\r\n @for (key of keys; track key.index) {\r\n <path [attr.d]=\"arcPath(KEY_SIG.inner, KEY_SIG.outer, key.index)\" class=\"keysig-segment\" />\r\n <text\r\n [attr.x]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).x\"\r\n [attr.y]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).y\"\r\n class=\"keysig-label\"\r\n >\r\n {{ getAccidentalText(key) }}\r\n </text>\r\n }\r\n\r\n <!-- \u2500\u2500 Major Keys Ring \u2500\u2500 -->\r\n @for (key of keys; track key.index) {\r\n <g\r\n [ngClass]=\"['major-group', getMajorState(key.index)]\"\r\n (click)=\"selectKey(key.index, 'major')\"\r\n >\r\n <path [attr.d]=\"arcPath(MAJOR.inner, MAJOR.outer, key.index)\" />\r\n\r\n <text\r\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\r\n [attr.y]=\"\r\n textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y +\r\n (getMajorRole(key.index) ? -9 : 0)\r\n \"\r\n class=\"major-label\"\r\n >\r\n {{ key.major }}\r\n </text>\r\n\r\n @if (getMajorRole(key.index)) {\r\n <text\r\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\r\n [attr.y]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y + 11\"\r\n class=\"role-label\"\r\n >\r\n {{ getMajorRole(key.index) }}\r\n </text>\r\n }\r\n </g>\r\n }\r\n\r\n <!-- \u2500\u2500 Minor Keys Ring \u2500\u2500 -->\r\n @for (key of keys; track key.index) {\r\n <g\r\n [ngClass]=\"['minor-group', getMinorState(key.index)]\"\r\n (click)=\"selectKey(key.index, 'minor')\"\r\n >\r\n <path [attr.d]=\"arcPath(MINOR.inner, MINOR.outer, key.index)\" />\r\n\r\n <text\r\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\r\n [attr.y]=\"\r\n textPos((MINOR.inner + MINOR.outer) / 2, key.index).y +\r\n (getMinorRole(key.index) ? -8 : 0)\r\n \"\r\n class=\"minor-label\"\r\n >\r\n {{ key.minor }}\r\n </text>\r\n\r\n @if (getMinorRole(key.index)) {\r\n <text\r\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\r\n [attr.y]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).y + 9\"\r\n class=\"role-label\"\r\n >\r\n {{ getMinorRole(key.index) }}\r\n </text>\r\n }\r\n </g>\r\n }\r\n\r\n <!-- \u2500\u2500 Outer border \u2500\u2500 -->\r\n <circle cx=\"300\" cy=\"300\" r=\"289\" class=\"outer-border\" />\r\n\r\n <!-- \u2500\u2500 Center Circle \u2500\u2500 -->\r\n <circle cx=\"300\" cy=\"300\" r=\"99\" class=\"center-circle\" />\r\n\r\n @if (selectedInfo) {\r\n <text x=\"300\" y=\"283\" class=\"center-name\">{{ selectedInfo.name }}</text>\r\n <text x=\"300\" y=\"304\" class=\"center-scale\">{{ selectedInfo.scale }}</text>\r\n <text x=\"300\" y=\"322\" class=\"center-relative\">{{ selectedInfo.relativeKey }}</text>\r\n <text x=\"300\" y=\"338\" class=\"center-deselect\">{{ text().deselect }}</text>\r\n } @else {\r\n <text x=\"300\" y=\"293\" class=\"center-hint\">{{ text().clickKey }}</text>\r\n <text x=\"300\" y=\"312\" class=\"center-hint-sm\">{{ text().seeChords }}</text>\r\n }\r\n </svg>\r\n </div>\r\n\r\n <!-- \u2500\u2500 Legend \u2500\u2500 -->\r\n <div class=\"legend\">\r\n <span class=\"legend-item tonic\">{{ text().tonicLegend }}</span>\r\n <span class=\"legend-item scale-major\">{{ text().majorLegend }}</span>\r\n <span class=\"legend-item scale-minor\">{{ text().minorLegend }}</span>\r\n <span class=\"legend-item scale-diminished\">{{ text().diminishedLegend }}</span>\r\n </div>\r\n\r\n <chord-section [chords]=\"chordNames\" [language]=\"language()\"></chord-section>\r\n\r\n <!-- \u2500\u2500 Chord Progressions \u2500\u2500 -->\r\n @if (progressions.length) {\r\n <section class=\"progressions-section\">\r\n <div class=\"progressions-header\">\r\n <h2>{{ text().commonProgressions }} {{ selectedInfo?.fullName }}</h2>\r\n <button class=\"btn-randomize\" (click)=\"randomizeProgressions()\">{{ text().generateProgressions }}</button>\r\n </div>\r\n <div class=\"progression-list\">\r\n @for (prog of progressions; track prog.name) {\r\n <div class=\"progression-card\">\r\n <div class=\"prog-header\">\r\n <span class=\"prog-mood\">{{ prog.mood }}</span>\r\n <span class=\"prog-name\">{{ prog.name }}</span>\r\n <span class=\"prog-genre\">{{ prog.genre }}</span>\r\n </div>\r\n <div class=\"prog-chords\">\r\n @for (chord of prog.chords; track $index; let last = $last) {\r\n <span class=\"prog-chord\">{{ chord }}</span>\r\n @if (!last) {\r\n <span class=\"prog-arrow\">\u2192</span>\r\n }\r\n }\r\n </div>\r\n <div class=\"prog-numerals\">\r\n @for (num of prog.numerals; track $index; let last = $last) {\r\n <span>{{ num }}</span>\r\n @if (!last) {\r\n <span class=\"prog-arrow\">\u2192</span>\r\n }\r\n }\r\n </div>\r\n <div class=\"prog-expand-row\">\r\n <button class=\"btn-expand\" (click)=\"toggleCard(prog.name)\">\r\n {{ expandedCards().has(prog.name) ? text().collapse : text().expand }}\r\n </button>\r\n <button class=\"btn-expand\" (click)=\"copyChords(prog)\">\r\n {{ text().copy }}\r\n </button>\r\n </div>\r\n @if (expandedCards().has(prog.name)) {\r\n <div class=\"prog-sections\">\r\n @for (section of prog.sections; track section.label) {\r\n <div class=\"prog-section\">\r\n <div class=\"prog-section-label\">{{ section.label }}</div>\r\n <div class=\"prog-chords\">\r\n @for (chord of section.chords; track $index; let last = $last) {\r\n <span class=\"prog-chord\">{{ chord }}</span>\r\n @if (!last) { <span class=\"prog-arrow\">\u2192</span> }\r\n }\r\n </div>\r\n <div class=\"prog-numerals\">\r\n @for (num of section.numerals; track $index; let last = $last) {\r\n <span>{{ num }}</span>\r\n @if (!last) { <span class=\"prog-arrow\">\u2192</span> }\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </section>\r\n }\r\n</div>\r\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"] }]
|
|
517
|
-
}], propDecorators: { language: [{ type: i0.Input, args: [{ isSignal: true, alias: "language", required: false }] }] } });
|
|
605
|
+
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-transpose-row\">\n <label class=\"prog-transpose-label\">{{ text().transposeLabel }}</label>\n <select class=\"prog-transpose-select\" (change)=\"setTranspose(prog.nameEn, +$any($event.target).value)\">\n @for (key of transposeKeys; track key.idx) {\n <option [value]=\"key.idx\" [selected]=\"key.idx === prog.transposeIdx\">{{ key.name }}</option>\n }\n </select>\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 <button class=\"btn-spice\" [class.btn-spice--active]=\"prog.spiceLevel > 0\" (click)=\"cycleSpice(prog.nameEn)\">\n {{ text().spiceLabels[prog.spiceLevel] }}\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-transpose-row{display:flex;align-items:center;gap:6px;margin-top:8px}.prog-transpose-label{font-size:.68rem;font-weight:600;color:var(--_chords-muted);white-space:nowrap}.prog-transpose-select{font-size:.72rem;font-weight:600;color:var(--_chords-muted);background:transparent;border:1px solid var(--_chords-border);border-radius:8px;padding:2px 6px;cursor:pointer;transition:border-color .15s,color .15s;appearance:auto}.prog-transpose-select:hover,.prog-transpose-select:focus{border-color:var(--_chords-primary);color:var(--_chords-primary);outline:none}.prog-expand-row{display:flex;justify-content:flex-end;margin-top:10px}.btn-spice{background:transparent;border:1px solid var(--cof-accent, #e09030);border-radius:6px;color:var(--cof-accent, #e09030);cursor:pointer;font-size:.75rem;padding:4px 10px;transition:background .2s,color .2s}.btn-spice:hover{background:var(--cof-accent, #e09030);color:#fff}.btn-spice.btn-spice--active{background:var(--cof-accent, #e09030);color:#fff;font-weight:600}.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"] }]
|
|
606
|
+
}], ctorParameters: () => [], propDecorators: { language: [{ type: i0.Input, args: [{ isSignal: true, alias: "language", required: false }] }] } });
|
|
518
607
|
|
|
519
608
|
/*
|
|
520
609
|
* Public API Surface of circle-of-fifths-lib
|
|
@@ -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/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';\r\nimport { NgClass } from '@angular/common';\r\n\r\ninterface CircleKey {\r\n index: number;\r\n major: string;\r\n minor: string;\r\n sharps: number;\r\n flats: number;\r\n}\r\n\r\ninterface ChordRow {\r\n numeral: string;\r\n chord: string;\r\n chordType: string;\r\n role: string;\r\n state: 'tonic' | 'scale-major' | 'scale-minor' | 'scale-diminished';\r\n}\r\n\r\ninterface Section {\r\n label: string;\r\n numerals: string[];\r\n chords: string[];\r\n}\r\n\r\ninterface Progression {\r\n name: string;\r\n mood: string;\r\n genre: string;\r\n numerals: string[];\r\n chords: string[];\r\n sections: Section[];\r\n}\r\n\r\ntype Language = 'en' | 'es';\r\ntype LocalizedText = Record<Language, string>;\r\n\r\ninterface ProgressionDefinition {\r\n name: LocalizedText;\r\n mood: LocalizedText;\r\n genre: LocalizedText;\r\n numerals: string[];\r\n}\r\n\r\nconst PROGRESSIONS: Record<'major' | 'minor', ProgressionDefinition[]> = {\r\n major: [\r\n {\r\n name: { en: 'Anthemic', es: 'Épica' },\r\n mood: { en: '😄 Joyful', es: '😄 Alegre' },\r\n genre: { en: 'Folk, Rock', es: 'Folk, rock' },\r\n numerals: ['I', 'IV', 'V', 'I'],\r\n },\r\n {\r\n name: { en: 'Hopeful', es: 'Esperanzadora' },\r\n mood: { en: '✨ Hopeful', es: '✨ Esperanzadora' },\r\n genre: { en: 'Pop, Indie', es: 'Pop, indie' },\r\n numerals: ['I', 'V', 'vi', 'IV'],\r\n },\r\n {\r\n name: { en: 'Melancholic', es: 'Melancólica' },\r\n mood: { en: '😢 Emotional', es: '😢 Emocional' },\r\n genre: { en: 'Ballads, Pop', es: 'Baladas, pop' },\r\n numerals: ['vi', 'IV', 'I', 'V'],\r\n },\r\n {\r\n name: { en: 'Cinematic', es: 'Cinemática' },\r\n mood: { en: '🎬 Tense', es: '🎬 Tensa' },\r\n genre: { en: 'Film, Jazz', es: 'Cine, jazz' },\r\n numerals: ['ii', 'V', 'vii°', 'I'],\r\n },\r\n {\r\n name: { en: 'Nostalgic', es: 'Retro' },\r\n mood: { en: '🕺 Retro', es: '🕺 Retro' },\r\n genre: { en: 'Doo-Wop, Pop', es: 'Doo-Wop, pop' },\r\n numerals: ['I', 'vi', 'IV', 'V'],\r\n },\r\n {\r\n name: { en: 'Dreamy', es: 'Soñadora' },\r\n mood: { en: '🌸 Floaty', es: '🌸 Etérea' },\r\n genre: { en: 'J-Pop, Anime', es: 'J-Pop, anime' },\r\n numerals: ['IV', 'V', 'iii', 'vi'],\r\n },\r\n {\r\n name: { en: 'Gospel', es: 'Gospel' },\r\n mood: { en: '🙏 Uplifting', es: '🙏 Elevadora' },\r\n genre: { en: 'Gospel, Soul', es: 'Gospel, soul' },\r\n numerals: ['I', 'IV', 'ii', 'V'],\r\n },\r\n {\r\n name: { en: 'Bittersweet', es: 'Agridulce' },\r\n mood: { en: '🍂 Bittersweet', es: '🍂 Agridulce' },\r\n genre: { en: 'Alt Rock, Indie', es: 'Rock alternativo, indie' },\r\n numerals: ['I', 'iii', 'IV', 'V'],\r\n },\r\n ],\r\n minor: [\r\n {\r\n name: { en: 'Brooding', es: 'Sombría' },\r\n mood: { en: '🌑 Dark', es: '🌑 Oscura' },\r\n genre: { en: 'Rock, Metal', es: 'Rock, metal' },\r\n numerals: ['i', 'VII', 'VI', 'VII'],\r\n },\r\n {\r\n name: { en: 'Haunting', es: 'Inquietante' },\r\n mood: { en: '👻 Mysterious', es: '👻 Misteriosa' },\r\n genre: { en: 'Film, Gothic', es: 'Cine, gótico' },\r\n numerals: ['i', 'iv', 'VII', 'III'],\r\n },\r\n {\r\n name: { en: 'Driving', es: 'Impulsora' },\r\n mood: { en: '⚡ Urgent', es: '⚡ Urgente' },\r\n genre: { en: 'Pop, EDM', es: 'Pop, EDM' },\r\n numerals: ['i', 'VI', 'III', 'VII'],\r\n },\r\n {\r\n name: { en: 'Wistful', es: 'Nostálgica' },\r\n mood: { en: '🌙 Wistful', es: '🌙 Nostálgica' },\r\n genre: { en: 'Cinematic', es: 'Cinemática' },\r\n numerals: ['i', 'v', 'VI', 'VII'],\r\n },\r\n {\r\n name: { en: 'Flamenco', es: 'Flamenco' },\r\n mood: { en: '🌹 Passionate', es: '🌹 Apasionado' },\r\n genre: { en: 'Flamenco, World', es: 'Flamenco, mundial' },\r\n numerals: ['i', 'VII', 'VI', 'iv'],\r\n },\r\n {\r\n name: { en: 'Tragic', es: 'Trágica' },\r\n mood: { en: '💔 Tragic', es: '💔 Trágica' },\r\n genre: { en: 'Classical, Drama', es: 'Clásica, drama' },\r\n numerals: ['i', 'iv', 'v', 'i'],\r\n },\r\n {\r\n name: { en: 'Epic', es: 'Épica' },\r\n mood: { en: '⚔️ Heroic', es: '⚔️ Heroico' },\r\n genre: { en: 'Epic, Orchestral', es: 'Épica, orquestal' },\r\n numerals: ['i', 'III', 'VII', 'VI'],\r\n },\r\n {\r\n name: { en: 'Ethereal', es: 'Etérea' },\r\n mood: { en: '🌊 Hypnotic', es: '🌊 Hipnótica' },\r\n genre: { en: 'Ambient, Post-Rock', es: 'Ambient, post-rock' },\r\n numerals: ['i', 'VI', 'iv', 'VII'],\r\n },\r\n ],\r\n};\r\n\r\nconst COPY = {\r\n en: {\r\n title: 'Circle of Fifths',\r\n subtitle: 'Click any major or minor key to highlight its diatonic chords',\r\n ariaLabel: 'Circle of Fifths',\r\n clickKey: 'Click a key',\r\n seeChords: 'to see its chords',\r\n deselect: '↩ click to deselect',\r\n tonicLegend: 'Tonic (I / i)',\r\n majorLegend: 'Major chords (IV, V)',\r\n minorLegend: 'Minor chords (ii, iii, vi)',\r\n diminishedLegend: 'Diminished (vii° / ii°)',\r\n diatonicChords: 'Diatonic Chords',\r\n commonProgressions: 'Common Progressions in',\r\n generateProgressions: 'Shuffle',\r\n chorus: 'Chorus',\r\n bridge: 'Bridge',\r\n outro: 'Outro',\r\n verse: 'Verse',\r\n copy: 'Copy',\r\n expand: 'Expand',\r\n collapse: 'Collapse',\r\n major: 'Major',\r\n minor: 'Minor',\r\n diminished: 'diminished',\r\n relativeMinor: 'Relative minor',\r\n relativeMajor: 'Relative major',\r\n roles: {\r\n tonic: 'Tonic',\r\n supertonic: 'Supertonic',\r\n mediant: 'Mediant',\r\n subdominant: 'Subdominant',\r\n dominant: 'Dominant',\r\n submediant: 'Submediant',\r\n leadingTone: 'Leading Tone',\r\n subtonic: 'Subtonic',\r\n },\r\n },\r\n es: {\r\n title: 'Círculo de quintas',\r\n subtitle: 'Haz clic en una tonalidad mayor o menor para resaltar sus acordes diatónicos',\r\n ariaLabel: 'Círculo de quintas',\r\n clickKey: 'Haz clic en una tonalidad',\r\n seeChords: 'para ver sus acordes',\r\n deselect: '↩ haz clic para deseleccionar',\r\n tonicLegend: 'Tónica (I / i)',\r\n majorLegend: 'Acordes mayores (IV, V)',\r\n minorLegend: 'Acordes menores (ii, iii, vi)',\r\n diminishedLegend: 'Disminuido (vii° / ii°)',\r\n diatonicChords: 'Acordes diatónicos',\r\n commonProgressions: 'Progresiones comunes en',\r\n generateProgressions: 'Generar nuevos',\r\n chorus: 'Coro',\r\n bridge: 'Puente',\r\n outro: 'Final',\r\n verse: 'Estrofa',\r\n copy: 'Copiar',\r\n expand: 'Expandir',\r\n collapse: 'Colapsar',\r\n major: 'Mayor',\r\n minor: 'Menor',\r\n diminished: 'disminuido',\r\n relativeMinor: 'Relativa menor',\r\n relativeMajor: 'Relativa mayor',\r\n roles: {\r\n tonic: 'Tónica',\r\n supertonic: 'Supertónica',\r\n mediant: 'Mediante',\r\n subdominant: 'Subdominante',\r\n dominant: 'Dominante',\r\n submediant: 'Submediante',\r\n leadingTone: 'Sensible',\r\n subtonic: 'Subtónica',\r\n },\r\n },\r\n} as const;\r\n\r\nconst KEYS: CircleKey[] = [\r\n { index: 0, major: 'C', minor: 'Am', sharps: 0, flats: 0 },\r\n { index: 1, major: 'G', minor: 'Em', sharps: 1, flats: 0 },\r\n { index: 2, major: 'D', minor: 'Bm', sharps: 2, flats: 0 },\r\n { index: 3, major: 'A', minor: 'F♯m', sharps: 3, flats: 0 },\r\n { index: 4, major: 'E', minor: 'C♯m', sharps: 4, flats: 0 },\r\n { index: 5, major: 'B', minor: 'G♯m', sharps: 5, flats: 0 },\r\n { index: 6, major: 'F♯', minor: 'D♯m', sharps: 6, flats: 0 },\r\n { index: 7, major: 'D♭', minor: 'B♭m', sharps: 0, flats: 5 },\r\n { index: 8, major: 'A♭', minor: 'Fm', sharps: 0, flats: 4 },\r\n { index: 9, major: 'E♭', minor: 'Cm', sharps: 0, flats: 3 },\r\n { index: 10, major: 'B♭', minor: 'Gm', sharps: 0, flats: 2 },\r\n { index: 11, major: 'F', minor: 'Dm', sharps: 0, flats: 1 },\r\n];\r\n\r\nfunction dimChord(idx: number): string {\r\n // The diminished chord root is the minor key two steps clockwise (idx+2)\r\n return KEYS[(idx + 2) % 12].minor.replace(/m$/, '°');\r\n}\r\n\r\nfunction buildChordRows(idx: number, type: 'major' | 'minor', copy: (typeof COPY)[Language]): ChordRow[] {\r\n const prev = (idx - 1 + 12) % 12;\r\n const next = (idx + 1) % 12;\r\n if (type === 'major') {\r\n return [\r\n { numeral: 'I', chord: KEYS[idx].major, chordType: copy.major, role: copy.roles.tonic, state: 'tonic' },\r\n { numeral: 'ii', chord: KEYS[prev].minor, chordType: copy.minor, role: copy.roles.supertonic, state: 'scale-minor' },\r\n { numeral: 'iii', chord: KEYS[next].minor, chordType: copy.minor, role: copy.roles.mediant, state: 'scale-minor' },\r\n { numeral: 'IV', chord: KEYS[prev].major, chordType: copy.major, role: copy.roles.subdominant, state: 'scale-major' },\r\n { numeral: 'V', chord: KEYS[next].major, chordType: copy.major, role: copy.roles.dominant, state: 'scale-major' },\r\n { numeral: 'vi', chord: KEYS[idx].minor, chordType: copy.minor, role: copy.roles.submediant, state: 'scale-minor' },\r\n { numeral: 'vii°', chord: dimChord(idx), chordType: copy.diminished, role: copy.roles.leadingTone, state: 'scale-diminished' },\r\n ];\r\n } else {\r\n return [\r\n { numeral: 'i', chord: KEYS[idx].minor, chordType: copy.minor, role: copy.roles.tonic, state: 'tonic' },\r\n { numeral: 'iv', chord: KEYS[prev].minor, chordType: copy.minor, role: copy.roles.subdominant, state: 'scale-minor' },\r\n { numeral: 'v', chord: KEYS[next].minor, chordType: copy.minor, role: copy.roles.dominant, state: 'scale-minor' },\r\n { numeral: 'III', chord: KEYS[idx].major, chordType: copy.major, role: copy.roles.mediant, state: 'scale-major' },\r\n { numeral: 'VI', chord: KEYS[prev].major, chordType: copy.major, role: copy.roles.submediant, state: 'scale-major' },\r\n { numeral: 'VII', chord: KEYS[next].major, chordType: copy.major, role: copy.roles.subtonic, state: 'scale-major' },\r\n { numeral: 'ii°', chord: dimChord(idx), chordType: copy.diminished, role: copy.roles.supertonic, state: 'scale-diminished' },\r\n ];\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'chord-section',\r\n standalone: true,\r\n imports: [NgClass],\r\n templateUrl: './chord-section.component.html',\r\n styleUrl: './chord-section.component.scss',\r\n})\r\nexport class ChordSectionComponent {\r\n readonly chords = input<string[]>([]);\r\n readonly language = input<Language>('en');\r\n readonly text = computed(() => COPY[this.language()]);\r\n\r\n readonly match = computed(() => {\r\n const input = this.chords();\r\n if (!input.length) return null;\r\n const inputSet = new Set(input);\r\n let best: { idx: number; type: 'major' | 'minor'; score: number } | null = null;\r\n for (let idx = 0; idx < 12; idx++) {\r\n for (const type of ['major', 'minor'] as const) {\r\n // chord names are language-independent; COPY.en is a dummy for label fields\r\n const score = buildChordRows(idx, type, COPY.en).filter((r) => inputSet.has(r.chord)).length;\r\n if (!best || score > best.score) best = { idx, type, score };\r\n }\r\n }\r\n return best && best.score > 0 ? best : null;\r\n });\r\n\r\n readonly rows = computed(() => {\r\n const m = this.match();\r\n if (!m) return [];\r\n return buildChordRows(m.idx, m.type, this.text());\r\n });\r\n\r\n readonly info = computed(() => {\r\n const m = this.match();\r\n if (!m) return null;\r\n const copy = this.text();\r\n const key = KEYS[m.idx];\r\n return {\r\n fullName: `${m.type === 'major' ? key.major : key.minor} ${m.type === 'major' ? copy.major : copy.minor}`,\r\n relativeKey: m.type === 'major'\r\n ? `${copy.relativeMinor}: ${key.minor}`\r\n : `${copy.relativeMajor}: ${key.major}`,\r\n };\r\n });\r\n}\r\n\r\n@Component({\r\n selector: 'the-chords-circle-of-fifths',\r\n standalone: true,\r\n imports: [NgClass, ChordSectionComponent],\r\n templateUrl: './circle-of-fifths.html',\r\n styleUrl: './circle-of-fifths.scss',\r\n host: { '[attr.lang]': 'language()' },\r\n})\r\nexport class CircleOfFifthsComponent {\r\n readonly language = input<Language>('en');\r\n readonly text = computed(() => COPY[this.language()]);\r\n readonly CX = 300;\r\n readonly CY = 300;\r\n\r\n readonly KEY_SIG = { inner: 250, outer: 288 };\r\n readonly MAJOR = { inner: 172, outer: 250 };\r\n readonly MINOR = { inner: 102, outer: 172 };\r\n\r\n readonly keys = KEYS;\r\n\r\n selectedIndex = signal<number | null>(null);\r\n selectedType = signal<'major' | 'minor' | null>(null);\r\n readonly activeProgressionDefs = signal<ProgressionDefinition[] | null>(null);\r\n readonly expandedCards = signal<Set<string>>(new Set());\r\n\r\n private get slice() {\r\n const idx = this.selectedIndex();\r\n if (idx === null) return null;\r\n return {\r\n prev: (idx - 1 + 12) % 12,\r\n curr: idx,\r\n next: (idx + 1) % 12,\r\n };\r\n }\r\n\r\n selectKey(index: number, type: 'major' | 'minor'): void {\r\n if (this.selectedIndex() === index && this.selectedType() === type) {\r\n this.selectedIndex.set(null);\r\n this.selectedType.set(null);\r\n } else {\r\n this.selectedIndex.set(index);\r\n this.selectedType.set(type);\r\n }\r\n this.activeProgressionDefs.set(null);\r\n this.expandedCards.set(new Set());\r\n }\r\n\r\n randomizeProgressions(): void {\r\n const type = this.selectedType();\r\n if (!type) return;\r\n // ponytail: sort-shuffle is fine for UI randomness\r\n this.activeProgressionDefs.set([...PROGRESSIONS[type]].sort(() => Math.random() - 0.5).slice(0, 4));\r\n this.expandedCards.set(new Set());\r\n }\r\n\r\n toggleCard(name: string): void {\r\n this.expandedCards.update((set) => {\r\n const next = new Set(set);\r\n next.has(name) ? next.delete(name) : next.add(name);\r\n return next;\r\n });\r\n }\r\n\r\n copyChords(prog: { name: string; chords: string[]; sections: { label: string; chords: string[] }[] }): void {\r\n const text = this.expandedCards().has(prog.name)\r\n ? [`${this.text().verse}: ${prog.chords.join(', ')}`, ...prog.sections.map((s) => `${s.label}: ${s.chords.join(', ')}`)].join('; ')\r\n : prog.chords.join(', ');\r\n navigator.clipboard.writeText(text);\r\n }\r\n\r\n getMajorState(index: number): string {\r\n const s = this.slice;\r\n if (!s) return '';\r\n const type = this.selectedType();\r\n if (index === s.curr) return type === 'major' ? 'tonic' : 'scale-major';\r\n if (index === s.prev || index === s.next) return 'scale-major';\r\n return '';\r\n }\r\n\r\n getMinorState(index: number): string {\r\n const s = this.slice;\r\n if (!s) return '';\r\n const type = this.selectedType();\r\n const dimIdx = (s.curr + 2) % 12;\r\n if (index === s.curr) return type === 'minor' ? 'tonic' : 'scale-minor';\r\n if (index === s.prev || index === s.next) return 'scale-minor';\r\n if (index === dimIdx) return 'scale-diminished';\r\n return '';\r\n }\r\n\r\n getMajorRole(index: number): string {\r\n const s = this.slice;\r\n if (!s) return '';\r\n const type = this.selectedType();\r\n if (type === 'major') {\r\n if (index === s.curr) return 'I';\r\n if (index === s.prev) return 'IV';\r\n if (index === s.next) return 'V';\r\n } else {\r\n if (index === s.curr) return 'III';\r\n if (index === s.prev) return 'VI';\r\n if (index === s.next) return 'VII';\r\n }\r\n return '';\r\n }\r\n\r\n getMinorRole(index: number): string {\r\n const s = this.slice;\r\n if (!s) return '';\r\n const type = this.selectedType();\r\n const dimIdx = (s.curr + 2) % 12;\r\n if (type === 'major') {\r\n if (index === s.curr) return 'vi';\r\n if (index === s.prev) return 'ii';\r\n if (index === s.next) return 'iii';\r\n if (index === dimIdx) return 'vii°';\r\n } else {\r\n if (index === s.curr) return 'i';\r\n if (index === s.prev) return 'iv';\r\n if (index === s.next) return 'v';\r\n if (index === dimIdx) return 'ii°';\r\n }\r\n return '';\r\n }\r\n\r\n arcPath(innerR: number, outerR: number, index: number, gap = 1.5): string {\r\n const mid = index * 30 - 90;\r\n const s = (mid - 15 + gap) * (Math.PI / 180);\r\n const e = (mid + 15 - gap) * (Math.PI / 180);\r\n const cx = this.CX,\r\n cy = this.CY;\r\n const x1 = cx + outerR * Math.cos(s),\r\n y1 = cy + outerR * Math.sin(s);\r\n const x2 = cx + outerR * Math.cos(e),\r\n y2 = cy + outerR * Math.sin(e);\r\n const x3 = cx + innerR * Math.cos(e),\r\n y3 = cy + innerR * Math.sin(e);\r\n const x4 = cx + innerR * Math.cos(s),\r\n y4 = cy + innerR * Math.sin(s);\r\n const f = (n: number) => n.toFixed(2);\r\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`;\r\n }\r\n\r\n textPos(midR: number, index: number): { x: number; y: number } {\r\n const a = (index * 30 - 90) * (Math.PI / 180);\r\n return {\r\n x: this.CX + midR * Math.cos(a),\r\n y: this.CY + midR * Math.sin(a),\r\n };\r\n }\r\n\r\n\r\n get chordTable(): ChordRow[] {\r\n const idx = this.selectedIndex();\r\n const type = this.selectedType();\r\n if (idx === null || type === null) return [];\r\n return buildChordRows(idx, type, this.text());\r\n }\r\n\r\n get chordNames(): string[] {\r\n return this.chordTable.map((r) => r.chord);\r\n }\r\n\r\n getAccidentalText(key: CircleKey): string {\r\n if (key.sharps > 0) return `${key.sharps}♯`;\r\n if (key.flats > 0) return `${key.flats}♭`;\r\n return '—';\r\n }\r\n\r\n get selectedInfo() {\r\n const idx = this.selectedIndex();\r\n const type = this.selectedType();\r\n if (idx === null || type === null) return null;\r\n const key = KEYS[idx];\r\n const copy = this.text();\r\n const scale = type === 'major' ? copy.major : copy.minor;\r\n return {\r\n name: type === 'major' ? key.major : key.minor,\r\n scale,\r\n fullName: `${type === 'major' ? key.major : key.minor} ${scale}`,\r\n relativeKey:\r\n type === 'major'\r\n ? `${copy.relativeMinor}: ${key.minor}`\r\n : `${copy.relativeMajor}: ${key.major}`,\r\n };\r\n }\r\n\r\n private deriveSections(numerals: string[], lookup: Map<string, string>): Section[] {\r\n const type = this.selectedType()!;\r\n const copy = this.text();\r\n const resolve = (ns: string[]) => ({ numerals: ns, chords: ns.map((n) => lookup.get(n) ?? n) });\r\n\r\n // Chorus: rotate to lift chord (IV for major, VI for minor); else fixed fallback\r\n const chorusPivot = type === 'major' ? 'IV' : 'VI';\r\n const chorusIdx = numerals.indexOf(chorusPivot);\r\n const chorus = chorusIdx > 0\r\n ? resolve([...numerals.slice(chorusIdx), ...numerals.slice(0, chorusIdx)])\r\n : resolve(type === 'major' ? ['I', 'IV', 'V', 'I'] : ['i', 'VI', 'III', 'VII']);\r\n\r\n // Bridge: rotate to contrast chord (vi for major, III for minor); else fixed fallback\r\n const bridgePivot = type === 'major' ? 'vi' : 'III';\r\n const bridgeIdx = numerals.indexOf(bridgePivot);\r\n const bridge = bridgeIdx >= 0\r\n ? resolve([...numerals.slice(bridgeIdx), ...numerals.slice(0, bridgeIdx)])\r\n : resolve(type === 'major' ? ['vi', 'IV', 'ii', 'V'] : ['III', 'VII', 'VI', 'iv']);\r\n\r\n // Outro: fixed resolving pattern per mode\r\n const outro = resolve(type === 'major' ? ['I', 'V', 'IV', 'I'] : ['i', 'VII', 'VI', 'i']);\r\n\r\n return [\r\n { label: copy.chorus, ...chorus },\r\n { label: copy.bridge, ...bridge },\r\n { label: copy.outro, ...outro },\r\n ];\r\n }\r\n\r\n get progressions(): Progression[] {\r\n const table = this.chordTable;\r\n if (!table.length) return [];\r\n const lookup = new Map(table.map((r) => [r.numeral, r.chord]));\r\n const language = this.language();\r\n const type = this.selectedType() === 'major' ? 'major' : 'minor';\r\n const defs = this.activeProgressionDefs() ?? PROGRESSIONS[type].slice(0, 4);\r\n return defs.map((definition) => ({\r\n name: definition.name[language],\r\n mood: definition.mood[language],\r\n genre: definition.genre[language],\r\n numerals: definition.numerals,\r\n chords: definition.numerals.map((numeral) => lookup.get(numeral) ?? numeral),\r\n sections: this.deriveSections(definition.numerals, lookup),\r\n }));\r\n }\r\n}\r\n","@if (rows().length) {\r\n <section class=\"chord-section\">\r\n @if (info(); as info) {\r\n <h2>{{ info.fullName }} — {{ text().diatonicChords }}</h2>\r\n <p class=\"relative-label\">{{ info.relativeKey }}</p>\r\n }\r\n <div class=\"chord-grid\">\r\n @for (row of rows(); track row.numeral) {\r\n <div class=\"chord-card\" [ngClass]=\"row.state\">\r\n <div class=\"chord-numeral\">{{ row.numeral }}</div>\r\n <div class=\"chord-name\">{{ row.chord }}</div>\r\n <div class=\"chord-type\">{{ row.chordType }}</div>\r\n <div class=\"chord-role\">{{ row.role }}</div>\r\n </div>\r\n }\r\n </div>\r\n </section>\r\n}\r\n","<div class=\"page\">\r\n <header class=\"page-header\">\r\n <h1>{{ text().title }}</h1>\r\n <p>{{ text().subtitle }}</p>\r\n </header>\r\n\r\n <div class=\"circle-wrapper\">\r\n <svg\r\n viewBox=\"0 0 600 600\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n [attr.aria-label]=\"text().ariaLabel\"\r\n >\r\n <!-- ── Key Signature Ring ── -->\r\n @for (key of keys; track key.index) {\r\n <path [attr.d]=\"arcPath(KEY_SIG.inner, KEY_SIG.outer, key.index)\" class=\"keysig-segment\" />\r\n <text\r\n [attr.x]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).x\"\r\n [attr.y]=\"textPos((KEY_SIG.inner + KEY_SIG.outer) / 2, key.index).y\"\r\n class=\"keysig-label\"\r\n >\r\n {{ getAccidentalText(key) }}\r\n </text>\r\n }\r\n\r\n <!-- ── Major Keys Ring ── -->\r\n @for (key of keys; track key.index) {\r\n <g\r\n [ngClass]=\"['major-group', getMajorState(key.index)]\"\r\n (click)=\"selectKey(key.index, 'major')\"\r\n >\r\n <path [attr.d]=\"arcPath(MAJOR.inner, MAJOR.outer, key.index)\" />\r\n\r\n <text\r\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\r\n [attr.y]=\"\r\n textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y +\r\n (getMajorRole(key.index) ? -9 : 0)\r\n \"\r\n class=\"major-label\"\r\n >\r\n {{ key.major }}\r\n </text>\r\n\r\n @if (getMajorRole(key.index)) {\r\n <text\r\n [attr.x]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).x\"\r\n [attr.y]=\"textPos((MAJOR.inner + MAJOR.outer) / 2, key.index).y + 11\"\r\n class=\"role-label\"\r\n >\r\n {{ getMajorRole(key.index) }}\r\n </text>\r\n }\r\n </g>\r\n }\r\n\r\n <!-- ── Minor Keys Ring ── -->\r\n @for (key of keys; track key.index) {\r\n <g\r\n [ngClass]=\"['minor-group', getMinorState(key.index)]\"\r\n (click)=\"selectKey(key.index, 'minor')\"\r\n >\r\n <path [attr.d]=\"arcPath(MINOR.inner, MINOR.outer, key.index)\" />\r\n\r\n <text\r\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\r\n [attr.y]=\"\r\n textPos((MINOR.inner + MINOR.outer) / 2, key.index).y +\r\n (getMinorRole(key.index) ? -8 : 0)\r\n \"\r\n class=\"minor-label\"\r\n >\r\n {{ key.minor }}\r\n </text>\r\n\r\n @if (getMinorRole(key.index)) {\r\n <text\r\n [attr.x]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).x\"\r\n [attr.y]=\"textPos((MINOR.inner + MINOR.outer) / 2, key.index).y + 9\"\r\n class=\"role-label\"\r\n >\r\n {{ getMinorRole(key.index) }}\r\n </text>\r\n }\r\n </g>\r\n }\r\n\r\n <!-- ── Outer border ── -->\r\n <circle cx=\"300\" cy=\"300\" r=\"289\" class=\"outer-border\" />\r\n\r\n <!-- ── Center Circle ── -->\r\n <circle cx=\"300\" cy=\"300\" r=\"99\" class=\"center-circle\" />\r\n\r\n @if (selectedInfo) {\r\n <text x=\"300\" y=\"283\" class=\"center-name\">{{ selectedInfo.name }}</text>\r\n <text x=\"300\" y=\"304\" class=\"center-scale\">{{ selectedInfo.scale }}</text>\r\n <text x=\"300\" y=\"322\" class=\"center-relative\">{{ selectedInfo.relativeKey }}</text>\r\n <text x=\"300\" y=\"338\" class=\"center-deselect\">{{ text().deselect }}</text>\r\n } @else {\r\n <text x=\"300\" y=\"293\" class=\"center-hint\">{{ text().clickKey }}</text>\r\n <text x=\"300\" y=\"312\" class=\"center-hint-sm\">{{ text().seeChords }}</text>\r\n }\r\n </svg>\r\n </div>\r\n\r\n <!-- ── Legend ── -->\r\n <div class=\"legend\">\r\n <span class=\"legend-item tonic\">{{ text().tonicLegend }}</span>\r\n <span class=\"legend-item scale-major\">{{ text().majorLegend }}</span>\r\n <span class=\"legend-item scale-minor\">{{ text().minorLegend }}</span>\r\n <span class=\"legend-item scale-diminished\">{{ text().diminishedLegend }}</span>\r\n </div>\r\n\r\n <chord-section [chords]=\"chordNames\" [language]=\"language()\"></chord-section>\r\n\r\n <!-- ── Chord Progressions ── -->\r\n @if (progressions.length) {\r\n <section class=\"progressions-section\">\r\n <div class=\"progressions-header\">\r\n <h2>{{ text().commonProgressions }} {{ selectedInfo?.fullName }}</h2>\r\n <button class=\"btn-randomize\" (click)=\"randomizeProgressions()\">{{ text().generateProgressions }}</button>\r\n </div>\r\n <div class=\"progression-list\">\r\n @for (prog of progressions; track prog.name) {\r\n <div class=\"progression-card\">\r\n <div class=\"prog-header\">\r\n <span class=\"prog-mood\">{{ prog.mood }}</span>\r\n <span class=\"prog-name\">{{ prog.name }}</span>\r\n <span class=\"prog-genre\">{{ prog.genre }}</span>\r\n </div>\r\n <div class=\"prog-chords\">\r\n @for (chord of prog.chords; track $index; let last = $last) {\r\n <span class=\"prog-chord\">{{ chord }}</span>\r\n @if (!last) {\r\n <span class=\"prog-arrow\">→</span>\r\n }\r\n }\r\n </div>\r\n <div class=\"prog-numerals\">\r\n @for (num of prog.numerals; track $index; let last = $last) {\r\n <span>{{ num }}</span>\r\n @if (!last) {\r\n <span class=\"prog-arrow\">→</span>\r\n }\r\n }\r\n </div>\r\n <div class=\"prog-expand-row\">\r\n <button class=\"btn-expand\" (click)=\"toggleCard(prog.name)\">\r\n {{ expandedCards().has(prog.name) ? text().collapse : text().expand }}\r\n </button>\r\n <button class=\"btn-expand\" (click)=\"copyChords(prog)\">\r\n {{ text().copy }}\r\n </button>\r\n </div>\r\n @if (expandedCards().has(prog.name)) {\r\n <div class=\"prog-sections\">\r\n @for (section of prog.sections; track section.label) {\r\n <div class=\"prog-section\">\r\n <div class=\"prog-section-label\">{{ section.label }}</div>\r\n <div class=\"prog-chords\">\r\n @for (chord of section.chords; track $index; let last = $last) {\r\n <span class=\"prog-chord\">{{ chord }}</span>\r\n @if (!last) { <span class=\"prog-arrow\">→</span> }\r\n }\r\n </div>\r\n <div class=\"prog-numerals\">\r\n @for (num of section.numerals; track $index; let last = $last) {\r\n <span>{{ num }}</span>\r\n @if (!last) { <span class=\"prog-arrow\">→</span> }\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </section>\r\n }\r\n</div>\r\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":";;;;AA4CA,MAAM,YAAY,GAAuD;AACvE,IAAA,KAAK,EAAE;AACL,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,OAAO,EAAE;YACrC,IAAI,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1C,KAAK,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;YAC7C,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC;AAChC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,eAAe,EAAE;YAC5C,IAAI,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,iBAAiB,EAAE;YAChD,KAAK,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;YAC7C,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC;AACjC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;YAC9C,IAAI,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE;YAChD,KAAK,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE;YACjD,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC;AACjC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,EAAE;YAC3C,IAAI,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE;YACxC,KAAK,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE;YAC7C,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC;AACnC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,OAAO,EAAE;YACtC,IAAI,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE;YACxC,KAAK,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE;YACjD,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC;AACjC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE;YACtC,IAAI,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1C,KAAK,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE;YACjD,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC;AACnC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE;YACpC,IAAI,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE;YAChD,KAAK,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE;YACjD,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC;AACjC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,WAAW,EAAE;YAC5C,IAAI,EAAE,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,cAAc,EAAE;YAClD,KAAK,EAAE,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,yBAAyB,EAAE;YAC/D,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC;AAClC,SAAA;AACF,KAAA;AACD,IAAA,KAAK,EAAE;AACL,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE;YACvC,IAAI,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,WAAW,EAAE;YACxC,KAAK,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;YAC/C,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC;AACpC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE;YAC3C,IAAI,EAAE,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE,EAAE,eAAe,EAAE;YAClD,KAAK,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE;YACjD,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC;AACpC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,WAAW,EAAE;YACxC,IAAI,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE;YACzC,KAAK,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE;YACzC,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC;AACpC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE;YACzC,IAAI,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,eAAe,EAAE;YAC/C,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,EAAE;YAC5C,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC;AAClC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE;YACxC,IAAI,EAAE,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE,EAAE,eAAe,EAAE;YAClD,KAAK,EAAE,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,mBAAmB,EAAE;YACzD,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC;AACnC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE;YACrC,IAAI,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,EAAE;YAC3C,KAAK,EAAE,EAAE,EAAE,EAAE,kBAAkB,EAAE,EAAE,EAAE,gBAAgB,EAAE;YACvD,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC;AAChC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE;YACjC,IAAI,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,EAAE;YAC3C,KAAK,EAAE,EAAE,EAAE,EAAE,kBAAkB,EAAE,EAAE,EAAE,kBAAkB,EAAE;YACzD,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC;AACpC,SAAA;AACD,QAAA;YACE,IAAI,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,EAAE;YACtC,IAAI,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,cAAc,EAAE;YAC/C,KAAK,EAAE,EAAE,EAAE,EAAE,oBAAoB,EAAE,EAAE,EAAE,oBAAoB,EAAE;YAC7D,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC;AACnC,SAAA;AACF,KAAA;CACF;AAED,MAAM,IAAI,GAAG;AACX,IAAA,EAAE,EAAE;AACF,QAAA,KAAK,EAAE,kBAAkB;AACzB,QAAA,QAAQ,EAAE,+DAA+D;AACzE,QAAA,SAAS,EAAE,kBAAkB;AAC7B,QAAA,QAAQ,EAAE,aAAa;AACvB,QAAA,SAAS,EAAE,mBAAmB;AAC9B,QAAA,QAAQ,EAAE,qBAAqB;AAC/B,QAAA,WAAW,EAAE,eAAe;AAC5B,QAAA,WAAW,EAAE,sBAAsB;AACnC,QAAA,WAAW,EAAE,4BAA4B;AACzC,QAAA,gBAAgB,EAAE,yBAAyB;AAC3C,QAAA,cAAc,EAAE,iBAAiB;AACjC,QAAA,kBAAkB,EAAE,wBAAwB;AAC5C,QAAA,oBAAoB,EAAE,SAAS;AAC/B,QAAA,MAAM,EAAE,QAAQ;AAChB,QAAA,MAAM,EAAE,QAAQ;AAChB,QAAA,KAAK,EAAE,OAAO;AACd,QAAA,KAAK,EAAE,OAAO;AACd,QAAA,IAAI,EAAE,MAAM;AACZ,QAAA,MAAM,EAAE,QAAQ;AAChB,QAAA,QAAQ,EAAE,UAAU;AACpB,QAAA,KAAK,EAAE,OAAO;AACd,QAAA,KAAK,EAAE,OAAO;AACd,QAAA,UAAU,EAAE,YAAY;AACxB,QAAA,aAAa,EAAE,gBAAgB;AAC/B,QAAA,aAAa,EAAE,gBAAgB;AAC/B,QAAA,KAAK,EAAE;AACL,YAAA,KAAK,EAAE,OAAO;AACd,YAAA,UAAU,EAAE,YAAY;AACxB,YAAA,OAAO,EAAE,SAAS;AAClB,YAAA,WAAW,EAAE,aAAa;AAC1B,YAAA,QAAQ,EAAE,UAAU;AACpB,YAAA,UAAU,EAAE,YAAY;AACxB,YAAA,WAAW,EAAE,cAAc;AAC3B,YAAA,QAAQ,EAAE,UAAU;AACrB,SAAA;AACF,KAAA;AACD,IAAA,EAAE,EAAE;AACF,QAAA,KAAK,EAAE,oBAAoB;AAC3B,QAAA,QAAQ,EAAE,8EAA8E;AACxF,QAAA,SAAS,EAAE,oBAAoB;AAC/B,QAAA,QAAQ,EAAE,2BAA2B;AACrC,QAAA,SAAS,EAAE,sBAAsB;AACjC,QAAA,QAAQ,EAAE,+BAA+B;AACzC,QAAA,WAAW,EAAE,gBAAgB;AAC7B,QAAA,WAAW,EAAE,yBAAyB;AACtC,QAAA,WAAW,EAAE,+BAA+B;AAC5C,QAAA,gBAAgB,EAAE,yBAAyB;AAC3C,QAAA,cAAc,EAAE,oBAAoB;AACpC,QAAA,kBAAkB,EAAE,yBAAyB;AAC7C,QAAA,oBAAoB,EAAE,gBAAgB;AACtC,QAAA,MAAM,EAAE,MAAM;AACd,QAAA,MAAM,EAAE,QAAQ;AAChB,QAAA,KAAK,EAAE,OAAO;AACd,QAAA,KAAK,EAAE,SAAS;AAChB,QAAA,IAAI,EAAE,QAAQ;AACd,QAAA,MAAM,EAAE,UAAU;AAClB,QAAA,QAAQ,EAAE,UAAU;AACpB,QAAA,KAAK,EAAE,OAAO;AACd,QAAA,KAAK,EAAE,OAAO;AACd,QAAA,UAAU,EAAE,YAAY;AACxB,QAAA,aAAa,EAAE,gBAAgB;AAC/B,QAAA,aAAa,EAAE,gBAAgB;AAC/B,QAAA,KAAK,EAAE;AACL,YAAA,KAAK,EAAE,QAAQ;AACf,YAAA,UAAU,EAAE,aAAa;AACzB,YAAA,OAAO,EAAE,UAAU;AACnB,YAAA,WAAW,EAAE,cAAc;AAC3B,YAAA,QAAQ,EAAE,WAAW;AACrB,YAAA,UAAU,EAAE,aAAa;AACzB,YAAA,WAAW,EAAE,UAAU;AACvB,YAAA,QAAQ,EAAE,WAAW;AACtB,SAAA;AACF,KAAA;CACO;AAEV,MAAM,IAAI,GAAgB;AACxB,IAAA,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;AAC1D,IAAA,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;AAC1D,IAAA,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;AAC1D,IAAA,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;AAC3D,IAAA,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;AAC3D,IAAA,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;AAC3D,IAAA,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;AAC5D,IAAA,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;AAC5D,IAAA,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;AAC3D,IAAA,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;AAC3D,IAAA,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;AAC5D,IAAA,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;CAC5D;AAED,SAAS,QAAQ,CAAC,GAAW,EAAA;;AAE3B,IAAA,OAAO,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC;AACtD;AAEA,SAAS,cAAc,CAAC,GAAW,EAAE,IAAuB,EAAE,IAA6B,EAAA;IACzF,MAAM,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE;IAChC,MAAM,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE;AAC3B,IAAA,IAAI,IAAI,KAAK,OAAO,EAAE;QACpB,OAAO;AACL,YAAA,EAAE,OAAO,EAAE,GAAG,EAAK,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAG,SAAS,EAAE,IAAI,CAAC,KAAK,EAAO,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAQ,KAAK,EAAE,OAAO,EAAE;AACtH,YAAA,EAAE,OAAO,EAAE,IAAI,EAAI,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,EAAO,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,EAAG,KAAK,EAAE,aAAa,EAAE;AAC5H,YAAA,EAAE,OAAO,EAAE,KAAK,EAAG,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,EAAO,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAM,KAAK,EAAE,aAAa,EAAE;AAC5H,YAAA,EAAE,OAAO,EAAE,IAAI,EAAI,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,EAAO,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE;AAC5H,YAAA,EAAE,OAAO,EAAE,GAAG,EAAK,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,EAAO,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAK,KAAK,EAAE,aAAa,EAAE;AAC5H,YAAA,EAAE,OAAO,EAAE,IAAI,EAAI,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAG,SAAS,EAAE,IAAI,CAAC,KAAK,EAAO,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,EAAG,KAAK,EAAE,aAAa,EAAE;AAC5H,YAAA,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAK,SAAS,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,kBAAkB,EAAE;SAClI;IACH;SAAO;QACL,OAAO;AACL,YAAA,EAAE,OAAO,EAAE,GAAG,EAAI,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAG,SAAS,EAAE,IAAI,CAAC,KAAK,EAAO,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAQ,KAAK,EAAE,OAAO,EAAE;AACrH,YAAA,EAAE,OAAO,EAAE,IAAI,EAAG,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,EAAO,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE;AAC3H,YAAA,EAAE,OAAO,EAAE,GAAG,EAAI,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,EAAO,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAK,KAAK,EAAE,aAAa,EAAE;AAC3H,YAAA,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAG,SAAS,EAAE,IAAI,CAAC,KAAK,EAAO,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAM,KAAK,EAAE,aAAa,EAAE;AAC3H,YAAA,EAAE,OAAO,EAAE,IAAI,EAAG,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,EAAO,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,EAAG,KAAK,EAAE,aAAa,EAAE;AAC3H,YAAA,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,EAAO,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAK,KAAK,EAAE,aAAa,EAAE;AAC3H,YAAA,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAK,SAAS,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,EAAG,KAAK,EAAE,kBAAkB,EAAE;SACjI;IACH;AACF;MASa,qBAAqB,CAAA;IACvB,MAAM,GAAG,KAAK,CAAW,EAAE;+EAAC;IAC5B,QAAQ,GAAG,KAAK,CAAW,IAAI;iFAAC;AAChC,IAAA,IAAI,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;6EAAC;AAE5C,IAAA,KAAK,GAAG,QAAQ,CAAC,MAAK;AAC7B,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE;QAC3B,IAAI,CAAC,KAAK,CAAC,MAAM;AAAE,YAAA,OAAO,IAAI;AAC9B,QAAA,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC;QAC/B,IAAI,IAAI,GAAmE,IAAI;AAC/E,QAAA,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,EAAE,EAAE;YACjC,KAAK,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,OAAO,CAAU,EAAE;;AAE9C,gBAAA,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM;AAC5F,gBAAA,IAAI,CAAC,IAAI,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK;oBAAE,IAAI,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE;YAC9D;QACF;AACA,QAAA,OAAO,IAAI,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI;IAC7C,CAAC;8EAAC;AAEO,IAAA,IAAI,GAAG,QAAQ,CAAC,MAAK;AAC5B,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;AACtB,QAAA,IAAI,CAAC,CAAC;AAAE,YAAA,OAAO,EAAE;AACjB,QAAA,OAAO,cAAc,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IACnD,CAAC;6EAAC;AAEO,IAAA,IAAI,GAAG,QAAQ,CAAC,MAAK;AAC5B,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;AACtB,QAAA,IAAI,CAAC,CAAC;AAAE,YAAA,OAAO,IAAI;AACnB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;QACvB,OAAO;AACL,YAAA,QAAQ,EAAE,CAAA,EAAG,CAAC,CAAC,IAAI,KAAK,OAAO,GAAG,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAA,CAAA,EAAI,CAAC,CAAC,IAAI,KAAK,OAAO,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA,CAAE;AACzG,YAAA,WAAW,EAAE,CAAC,CAAC,IAAI,KAAK;kBACpB,GAAG,IAAI,CAAC,aAAa,CAAA,EAAA,EAAK,GAAG,CAAC,KAAK,CAAA;kBACnC,GAAG,IAAI,CAAC,aAAa,CAAA,EAAA,EAAK,GAAG,CAAC,KAAK,CAAA,CAAE;SAC1C;IACH,CAAC;6EAAC;uGArCS,qBAAqB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAArB,qBAAqB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECrRlC,ksBAkBA,EAAA,MAAA,EAAA,CAAA,kvFAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,ED+PY,OAAO,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,SAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;2FAIN,qBAAqB,EAAA,UAAA,EAAA,CAAA;kBAPjC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,eAAe,EAAA,UAAA,EACb,IAAI,EAAA,OAAA,EACP,CAAC,OAAO,CAAC,EAAA,QAAA,EAAA,ksBAAA,EAAA,MAAA,EAAA,CAAA,kvFAAA,CAAA,EAAA;;MAoDP,uBAAuB,CAAA;IACzB,QAAQ,GAAG,KAAK,CAAW,IAAI;iFAAC;AAChC,IAAA,IAAI,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;6EAAC;IAC5C,EAAE,GAAG,GAAG;IACR,EAAE,GAAG,GAAG;IAER,OAAO,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE;IACpC,KAAK,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE;IAClC,KAAK,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE;IAElC,IAAI,GAAG,IAAI;IAEpB,aAAa,GAAG,MAAM,CAAgB,IAAI;sFAAC;IAC3C,YAAY,GAAG,MAAM,CAA2B,IAAI;qFAAC;IAC5C,qBAAqB,GAAG,MAAM,CAAiC,IAAI;8FAAC;AACpE,IAAA,aAAa,GAAG,MAAM,CAAc,IAAI,GAAG,EAAE;sFAAC;AAEvD,IAAA,IAAY,KAAK,GAAA;AACf,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,EAAE;QAChC,IAAI,GAAG,KAAK,IAAI;AAAE,YAAA,OAAO,IAAI;QAC7B,OAAO;YACL,IAAI,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE;AACzB,YAAA,IAAI,EAAE,GAAG;AACT,YAAA,IAAI,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE;SACrB;IACH;IAEA,SAAS,CAAC,KAAa,EAAE,IAAuB,EAAA;AAC9C,QAAA,IAAI,IAAI,CAAC,aAAa,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;AAClE,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;AAC5B,YAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;QAC7B;aAAO;AACL,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC;AAC7B,YAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;QAC7B;AACA,QAAA,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC;QACpC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC;IACnC;IAEA,qBAAqB,GAAA;AACnB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE;AAChC,QAAA,IAAI,CAAC,IAAI;YAAE;;AAEX,QAAA,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACnG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC;IACnC;AAEA,IAAA,UAAU,CAAC,IAAY,EAAA;QACrB,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,KAAI;AAChC,YAAA,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC;YACzB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;AACnD,YAAA,OAAO,IAAI;AACb,QAAA,CAAC,CAAC;IACJ;AAEA,IAAA,UAAU,CAAC,IAAyF,EAAA;AAClG,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI;cAC3C,CAAC,CAAA,EAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAA,EAAA,EAAK,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAE,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAA,EAAG,CAAC,CAAC,KAAK,CAAA,EAAA,EAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI;cAChI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;AAC1B,QAAA,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC;IACrC;AAEA,IAAA,aAAa,CAAC,KAAa,EAAA;AACzB,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK;AACpB,QAAA,IAAI,CAAC,CAAC;AAAE,YAAA,OAAO,EAAE;AACjB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE;AAChC,QAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;YAAE,OAAO,IAAI,KAAK,OAAO,GAAG,OAAO,GAAG,aAAa;QACvE,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,YAAA,OAAO,aAAa;AAC9D,QAAA,OAAO,EAAE;IACX;AAEA,IAAA,aAAa,CAAC,KAAa,EAAA;AACzB,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK;AACpB,QAAA,IAAI,CAAC,CAAC;AAAE,YAAA,OAAO,EAAE;AACjB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE;QAChC,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE;AAChC,QAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;YAAE,OAAO,IAAI,KAAK,OAAO,GAAG,OAAO,GAAG,aAAa;QACvE,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,YAAA,OAAO,aAAa;QAC9D,IAAI,KAAK,KAAK,MAAM;AAAE,YAAA,OAAO,kBAAkB;AAC/C,QAAA,OAAO,EAAE;IACX;AAEA,IAAA,YAAY,CAAC,KAAa,EAAA;AACxB,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK;AACpB,QAAA,IAAI,CAAC,CAAC;AAAE,YAAA,OAAO,EAAE;AACjB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE;AAChC,QAAA,IAAI,IAAI,KAAK,OAAO,EAAE;AACpB,YAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,gBAAA,OAAO,GAAG;AAChC,YAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,gBAAA,OAAO,IAAI;AACjC,YAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,gBAAA,OAAO,GAAG;QAClC;aAAO;AACL,YAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,gBAAA,OAAO,KAAK;AAClC,YAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,gBAAA,OAAO,IAAI;AACjC,YAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,gBAAA,OAAO,KAAK;QACpC;AACA,QAAA,OAAO,EAAE;IACX;AAEA,IAAA,YAAY,CAAC,KAAa,EAAA;AACxB,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK;AACpB,QAAA,IAAI,CAAC,CAAC;AAAE,YAAA,OAAO,EAAE;AACjB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE;QAChC,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE;AAChC,QAAA,IAAI,IAAI,KAAK,OAAO,EAAE;AACpB,YAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,gBAAA,OAAO,IAAI;AACjC,YAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,gBAAA,OAAO,IAAI;AACjC,YAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,gBAAA,OAAO,KAAK;YAClC,IAAI,KAAK,KAAK,MAAM;AAAE,gBAAA,OAAO,MAAM;QACrC;aAAO;AACL,YAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,gBAAA,OAAO,GAAG;AAChC,YAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,gBAAA,OAAO,IAAI;AACjC,YAAA,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI;AAAE,gBAAA,OAAO,GAAG;YAChC,IAAI,KAAK,KAAK,MAAM;AAAE,gBAAA,OAAO,KAAK;QACpC;AACA,QAAA,OAAO,EAAE;IACX;IAEA,OAAO,CAAC,MAAc,EAAE,MAAc,EAAE,KAAa,EAAE,GAAG,GAAG,GAAG,EAAA;AAC9D,QAAA,MAAM,GAAG,GAAG,KAAK,GAAG,EAAE,GAAG,EAAE;AAC3B,QAAA,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC,EAAE,GAAG,GAAG,CAAC;AAC5C,QAAA,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC,EAAE,GAAG,GAAG,CAAC;QAC5C,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,EAChB,EAAE,GAAG,IAAI,CAAC,EAAE;QACd,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAClC,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAChC,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAClC,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAChC,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAClC,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAChC,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAClC,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAChC,QAAA,MAAM,CAAC,GAAG,CAAC,CAAS,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACrC,OAAO,CAAA,CAAA,EAAI,CAAC,CAAC,EAAE,CAAC,CAAA,CAAA,EAAI,CAAC,CAAC,EAAE,CAAC,CAAA,EAAA,EAAK,MAAM,CAAA,CAAA,EAAI,MAAM,UAAU,CAAC,CAAC,EAAE,CAAC,CAAA,CAAA,EAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAA,CAAA,EAAI,CAAC,CAAC,EAAE,CAAC,CAAA,EAAA,EAAK,MAAM,CAAA,CAAA,EAAI,MAAM,CAAA,OAAA,EAAU,CAAC,CAAC,EAAE,CAAC,CAAA,CAAA,EAAI,CAAC,CAAC,EAAE,CAAC,CAAA,CAAA,CAAG;IAC3I;IAEA,OAAO,CAAC,IAAY,EAAE,KAAa,EAAA;AACjC,QAAA,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC,EAAE,GAAG,GAAG,CAAC;QAC7C,OAAO;AACL,YAAA,CAAC,EAAE,IAAI,CAAC,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/B,YAAA,CAAC,EAAE,IAAI,CAAC,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;SAChC;IACH;AAGA,IAAA,IAAI,UAAU,GAAA;AACZ,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,EAAE;AAChC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE;AAChC,QAAA,IAAI,GAAG,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI;AAAE,YAAA,OAAO,EAAE;QAC5C,OAAO,cAAc,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IAC/C;AAEA,IAAA,IAAI,UAAU,GAAA;AACZ,QAAA,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC;IAC5C;AAEA,IAAA,iBAAiB,CAAC,GAAc,EAAA;AAC9B,QAAA,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;AAAE,YAAA,OAAO,CAAA,EAAG,GAAG,CAAC,MAAM,GAAG;AAC3C,QAAA,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC;AAAE,YAAA,OAAO,CAAA,EAAG,GAAG,CAAC,KAAK,GAAG;AACzC,QAAA,OAAO,GAAG;IACZ;AAEA,IAAA,IAAI,YAAY,GAAA;AACd,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,EAAE;AAChC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE;AAChC,QAAA,IAAI,GAAG,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI;AAAE,YAAA,OAAO,IAAI;AAC9C,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;AACrB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;AACxB,QAAA,MAAM,KAAK,GAAG,IAAI,KAAK,OAAO,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK;QACxD,OAAO;AACL,YAAA,IAAI,EAAE,IAAI,KAAK,OAAO,GAAG,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK;YAC9C,KAAK;AACL,YAAA,QAAQ,EAAE,CAAA,EAAG,IAAI,KAAK,OAAO,GAAG,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAA,CAAA,EAAI,KAAK,CAAA,CAAE;YAChE,WAAW,EACT,IAAI,KAAK;kBACL,GAAG,IAAI,CAAC,aAAa,CAAA,EAAA,EAAK,GAAG,CAAC,KAAK,CAAA;kBACnC,GAAG,IAAI,CAAC,aAAa,CAAA,EAAA,EAAK,GAAG,CAAC,KAAK,CAAA,CAAE;SAC5C;IACH;IAEQ,cAAc,CAAC,QAAkB,EAAE,MAA2B,EAAA;AACpE,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAG;AACjC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;AACxB,QAAA,MAAM,OAAO,GAAG,CAAC,EAAY,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;;AAG/F,QAAA,MAAM,WAAW,GAAG,IAAI,KAAK,OAAO,GAAG,IAAI,GAAG,IAAI;QAClD,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC;AAC/C,QAAA,MAAM,MAAM,GAAG,SAAS,GAAG;cACvB,OAAO,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AACzE,cAAE,OAAO,CAAC,IAAI,KAAK,OAAO,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;;AAGjF,QAAA,MAAM,WAAW,GAAG,IAAI,KAAK,OAAO,GAAG,IAAI,GAAG,KAAK;QACnD,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC;AAC/C,QAAA,MAAM,MAAM,GAAG,SAAS,IAAI;cACxB,OAAO,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AACzE,cAAE,OAAO,CAAC,IAAI,KAAK,OAAO,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;;AAGpF,QAAA,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,KAAK,OAAO,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;QAEzF,OAAO;YACL,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE;YACjC,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE;YACjC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,KAAK,EAAE;SAChC;IACH;AAEA,IAAA,IAAI,YAAY,GAAA;AACd,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU;QAC7B,IAAI,CAAC,KAAK,CAAC,MAAM;AAAE,YAAA,OAAO,EAAE;QAC5B,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAC9D,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE;AAChC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,KAAK,OAAO,GAAG,OAAO,GAAG,OAAO;AAChE,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,qBAAqB,EAAE,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,UAAU,MAAM;AAC/B,YAAA,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC;AAC/B,YAAA,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC;AAC/B,YAAA,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC;YACjC,QAAQ,EAAE,UAAU,CAAC,QAAQ;YAC7B,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC;YAC5E,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC;AAC3D,SAAA,CAAC,CAAC;IACL;uGA/NW,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAvB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,uBAAuB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,6BAAA,EAAA,MAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,WAAA,EAAA,YAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EErUpC,o7OAoLA,EAAA,MAAA,EAAA,CAAA,qpQAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EF4IY,OAAO,oFA3CN,qBAAqB,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,UAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;2FAgDrB,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBARnC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,6BAA6B,EAAA,UAAA,EAC3B,IAAI,EAAA,OAAA,EACP,CAAC,OAAO,EAAE,qBAAqB,CAAC,EAAA,IAAA,EAGnC,EAAE,aAAa,EAAE,YAAY,EAAE,EAAA,QAAA,EAAA,o7OAAA,EAAA,MAAA,EAAA,CAAA,qpQAAA,CAAA,EAAA;;;AGnUvC;;AAEG;;ACFH;;AAEG;;;;"}
|
|
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, effect, 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 nameEn: string;\n name: string;\n mood: string;\n genre: string;\n numerals: string[];\n chords: string[];\n sections: Section[];\n transposeIdx: number;\n spiceLevel: number;\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 transposeLabel: 'Transpose to:',\n spiceLabels: ['🌶 Spice', '🌶🌶 Spicier', '🔥 Hot!', '↩ Plain'],\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 transposeLabel: 'Transponer a:',\n spiceLabels: ['🌶 Condimentar', '🌶🌶 Más sabor', '🔥 ¡Picante!', '↩ Simple'],\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// Suffix to append per numeral at spice levels 1, 2, 3.\n// Minor chords already carry 'm' (e.g. \"Dm\"), so suffix starts from the number.\n// Diminished chords carry '°' which gets replaced entirely.\nconst SPICE_SUFFIXES: Record<string, readonly [string, string, string]> = {\n 'I': ['maj7', 'maj9', 'maj9' ],\n 'ii': ['7', '9', '11' ],\n 'iii': ['7', '7', '7' ],\n 'IV': ['maj7', 'maj9', 'maj7♯11' ],\n 'V': ['7', '9', '13' ],\n 'vi': ['7', '9', '11' ],\n 'vii°': ['m7♭5', 'm7♭5', 'm7♭5' ],\n 'i': ['7', '9', '9' ],\n 'iv': ['7', '9', '11' ],\n 'v': ['7', '7', '7' ],\n 'III': ['maj7', 'maj7', 'maj7' ],\n 'VI': ['maj7', 'maj9', 'maj9' ],\n 'VII': ['7', '9', '13' ],\n 'ii°': ['m7♭5', 'm7♭5', 'm7♭5' ],\n};\n\nfunction spiceChord(chord: string, numeral: string, level: number): string {\n if (level === 0) return chord;\n const suffixes = SPICE_SUFFIXES[numeral];\n if (!suffixes) return chord;\n const suffix = suffixes[level - 1];\n // Diminished chords: strip '°', then the suffix already starts with 'm'\n return chord.endsWith('°') ? chord.slice(0, -1) + suffix : chord + suffix;\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 readonly transposeMap = signal<Map<string, number>>(new Map());\n readonly spiceMap = signal<Map<string, number>>(new Map());\n\n constructor() {\n // Restore selected key from URL hash (format: #{index}-{type})\n const [idxStr, type] = location.hash.slice(1).split('-');\n const idx = Number(idxStr);\n if (!isNaN(idx) && idx >= 0 && idx < 12 && (type === 'major' || type === 'minor')) {\n this.selectedIndex.set(idx);\n this.selectedType.set(type as 'major' | 'minor');\n }\n // Keep URL hash in sync with selected key\n effect(() => {\n const idx = this.selectedIndex();\n const type = this.selectedType();\n const hash = idx !== null && type !== null ? `#${idx}-${type}` : '';\n history.replaceState(null, '', location.pathname + location.search + hash);\n });\n }\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 this.transposeMap.set(new Map());\n this.spiceMap.set(new Map());\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 this.transposeMap.set(new Map());\n this.spiceMap.set(new Map());\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 setTranspose(nameEn: string, targetIdx: number): void {\n this.transposeMap.update((m) => {\n const next = new Map(m);\n targetIdx === this.selectedIndex() ? next.delete(nameEn) : next.set(nameEn, targetIdx);\n return next;\n });\n }\n\n cycleSpice(nameEn: string): void {\n this.spiceMap.update((m) => {\n const next = new Map(m);\n const nextLevel = ((next.get(nameEn) ?? 0) + 1) % 4;\n nextLevel === 0 ? next.delete(nameEn) : next.set(nameEn, nextLevel);\n return next;\n });\n }\n\n get transposeKeys(): { idx: number; name: string }[] {\n const type = this.selectedType();\n if (!type) return [];\n return KEYS.map((k) => ({ idx: k.index, name: type === 'major' ? k.major : k.minor }));\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 selIdx = this.selectedIndex()!;\n const transposeMap = this.transposeMap();\n const spiceMap = this.spiceMap();\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 const targetIdx = transposeMap.get(definition.name.en) ?? selIdx;\n const spiceLevel = spiceMap.get(definition.name.en) ?? 0;\n const baseRows = buildChordRows(targetIdx, type, this.text());\n const lookup = new Map(\n baseRows.map((r) => [r.numeral, spiceChord(r.chord, r.numeral, spiceLevel)]),\n );\n return {\n nameEn: definition.name.en,\n name: definition.name[language],\n mood: definition.mood[language],\n genre: definition.genre[language],\n numerals: definition.numerals,\n chords: definition.numerals.map((n) => lookup.get(n) ?? n),\n sections: this.deriveSections(definition.numerals, lookup),\n transposeIdx: targetIdx,\n spiceLevel,\n };\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-transpose-row\">\n <label class=\"prog-transpose-label\">{{ text().transposeLabel }}</label>\n <select class=\"prog-transpose-select\" (change)=\"setTranspose(prog.nameEn, +$any($event.target).value)\">\n @for (key of transposeKeys; track key.idx) {\n <option [value]=\"key.idx\" [selected]=\"key.idx === prog.transposeIdx\">{{ key.name }}</option>\n }\n </select>\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 <button class=\"btn-spice\" [class.btn-spice--active]=\"prog.spiceLevel > 0\" (click)=\"cycleSpice(prog.nameEn)\">\n {{ text().spiceLabels[prog.spiceLevel] }}\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.3.
|
|
3
|
+
"version": "0.3.2",
|
|
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"
|
|
@@ -20,12 +20,15 @@ interface Section {
|
|
|
20
20
|
chords: string[];
|
|
21
21
|
}
|
|
22
22
|
interface Progression {
|
|
23
|
+
nameEn: string;
|
|
23
24
|
name: string;
|
|
24
25
|
mood: string;
|
|
25
26
|
genre: string;
|
|
26
27
|
numerals: string[];
|
|
27
28
|
chords: string[];
|
|
28
29
|
sections: Section[];
|
|
30
|
+
transposeIdx: number;
|
|
31
|
+
spiceLevel: number;
|
|
29
32
|
}
|
|
30
33
|
type Language = 'en' | 'es';
|
|
31
34
|
type LocalizedText = Record<Language, string>;
|
|
@@ -59,6 +62,8 @@ declare class ChordSectionComponent {
|
|
|
59
62
|
readonly copy: "Copy";
|
|
60
63
|
readonly expand: "Expand";
|
|
61
64
|
readonly collapse: "Collapse";
|
|
65
|
+
readonly transposeLabel: "Transpose to:";
|
|
66
|
+
readonly spiceLabels: readonly ["🌶 Spice", "🌶🌶 Spicier", "🔥 Hot!", "↩ Plain"];
|
|
62
67
|
readonly major: "Major";
|
|
63
68
|
readonly minor: "Minor";
|
|
64
69
|
readonly diminished: "diminished";
|
|
@@ -95,6 +100,8 @@ declare class ChordSectionComponent {
|
|
|
95
100
|
readonly copy: "Copiar";
|
|
96
101
|
readonly expand: "Expandir";
|
|
97
102
|
readonly collapse: "Colapsar";
|
|
103
|
+
readonly transposeLabel: "Transponer a:";
|
|
104
|
+
readonly spiceLabels: readonly ["🌶 Condimentar", "🌶🌶 Más sabor", "🔥 ¡Picante!", "↩ Simple"];
|
|
98
105
|
readonly major: "Mayor";
|
|
99
106
|
readonly minor: "Menor";
|
|
100
107
|
readonly diminished: "disminuido";
|
|
@@ -147,6 +154,8 @@ declare class CircleOfFifthsComponent {
|
|
|
147
154
|
readonly copy: "Copy";
|
|
148
155
|
readonly expand: "Expand";
|
|
149
156
|
readonly collapse: "Collapse";
|
|
157
|
+
readonly transposeLabel: "Transpose to:";
|
|
158
|
+
readonly spiceLabels: readonly ["🌶 Spice", "🌶🌶 Spicier", "🔥 Hot!", "↩ Plain"];
|
|
150
159
|
readonly major: "Major";
|
|
151
160
|
readonly minor: "Minor";
|
|
152
161
|
readonly diminished: "diminished";
|
|
@@ -183,6 +192,8 @@ declare class CircleOfFifthsComponent {
|
|
|
183
192
|
readonly copy: "Copiar";
|
|
184
193
|
readonly expand: "Expandir";
|
|
185
194
|
readonly collapse: "Colapsar";
|
|
195
|
+
readonly transposeLabel: "Transponer a:";
|
|
196
|
+
readonly spiceLabels: readonly ["🌶 Condimentar", "🌶🌶 Más sabor", "🔥 ¡Picante!", "↩ Simple"];
|
|
186
197
|
readonly major: "Mayor";
|
|
187
198
|
readonly minor: "Menor";
|
|
188
199
|
readonly diminished: "disminuido";
|
|
@@ -218,6 +229,9 @@ declare class CircleOfFifthsComponent {
|
|
|
218
229
|
selectedType: _angular_core.WritableSignal<"major" | "minor" | null>;
|
|
219
230
|
readonly activeProgressionDefs: _angular_core.WritableSignal<ProgressionDefinition[] | null>;
|
|
220
231
|
readonly expandedCards: _angular_core.WritableSignal<Set<string>>;
|
|
232
|
+
readonly transposeMap: _angular_core.WritableSignal<Map<string, number>>;
|
|
233
|
+
readonly spiceMap: _angular_core.WritableSignal<Map<string, number>>;
|
|
234
|
+
constructor();
|
|
221
235
|
private get slice();
|
|
222
236
|
selectKey(index: number, type: 'major' | 'minor'): void;
|
|
223
237
|
randomizeProgressions(): void;
|
|
@@ -230,6 +244,12 @@ declare class CircleOfFifthsComponent {
|
|
|
230
244
|
chords: string[];
|
|
231
245
|
}[];
|
|
232
246
|
}): void;
|
|
247
|
+
setTranspose(nameEn: string, targetIdx: number): void;
|
|
248
|
+
cycleSpice(nameEn: string): void;
|
|
249
|
+
get transposeKeys(): {
|
|
250
|
+
idx: number;
|
|
251
|
+
name: string;
|
|
252
|
+
}[];
|
|
233
253
|
getMajorState(index: number): string;
|
|
234
254
|
getMinorState(index: number): string;
|
|
235
255
|
getMajorRole(index: number): string;
|