@musodojo/music-theory-data 14.0.2 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@musodojo/music-theory-data",
3
- "version": "14.0.2",
3
+ "version": "15.0.0",
4
4
  "description": "The musician-friendly TypeScript library for modes, scales, chords, and more.",
5
5
  "keywords": [
6
6
  "music",
@@ -10,4 +10,4 @@
10
10
  "author": "Conor Dowdall",
11
11
  "type": "module",
12
12
  "main": "src/mod.ts"
13
- }
13
+ }
@@ -0,0 +1,82 @@
1
+ import type { NoteCollection } from "../../types/note-collections.d.ts";
2
+
3
+ const augmentedTriad: NoteCollection = {
4
+ primaryName: "aug",
5
+ names: ["aug", "+", "Augmented Triad"],
6
+ intervals: ["1", "3", "♯5"],
7
+ integers: [0, 4, 8],
8
+ type: ["augmented", "chord", "arpeggio", "triad", "symmetrical"],
9
+ characteristics: ["tense", "unstable", "dreamy", "dissonant"],
10
+ pattern: ["major third", "major third"],
11
+ patternShort: ["M3", "M3"],
12
+ } as const;
13
+
14
+ const italian6: NoteCollection = {
15
+ primaryName: "It+6",
16
+ names: ["It+6", "Italian 6th"],
17
+ intervals: ["1", "3", "♭6"],
18
+ integers: [0, 4, 8],
19
+ type: ["augmented", "chord", "arpeggio", "triad", "classical"],
20
+ characteristics: [
21
+ "classical harmony",
22
+ "pre-dominant function",
23
+ "chromatic",
24
+ ],
25
+ pattern: ["major third", "diminished fourth"],
26
+ patternShort: ["M3", "d4"],
27
+ } as const;
28
+
29
+ const french6: NoteCollection = {
30
+ primaryName: "Fr+6",
31
+ names: ["Fr+6", "French 6th"],
32
+ intervals: ["1", "3", "♯4", "♭6"],
33
+ integers: [0, 4, 6, 8],
34
+ type: ["augmented", "chord", "arpeggio", "tetrad", "classical"],
35
+ characteristics: [
36
+ "classical harmony",
37
+ "pre-dominant function",
38
+ "chromatic",
39
+ "contains a tritone",
40
+ ],
41
+ pattern: ["major third", "minor second", "major second"],
42
+ patternShort: ["M3", "m2", "M2"],
43
+ } as const;
44
+
45
+ const german6: NoteCollection = {
46
+ primaryName: "Ger+6",
47
+ names: ["Ger+6", "German 6th"],
48
+ intervals: ["1", "3", "5", "♭6"],
49
+ integers: [0, 4, 7, 8],
50
+ type: ["augmented", "chord", "arpeggio", "tetrad", "classical"],
51
+ characteristics: [
52
+ "classical harmony",
53
+ "pre-dominant function",
54
+ "chromatic",
55
+ "enharmonically equivalent to a dominant 7th",
56
+ ],
57
+ pattern: ["major third", "minor third", "augmented unison"],
58
+ patternShort: ["M3", "m3", "A1"],
59
+ } as const;
60
+
61
+ const augmented7: NoteCollection = {
62
+ primaryName: "aug7",
63
+ names: ["aug7", "+7", "7♯5", "Augmented Seventh"],
64
+ intervals: ["1", "3", "♯5", "♭7"],
65
+ integers: [0, 4, 8, 10],
66
+ type: ["augmented", "dominant", "chord", "arpeggio", "tetrad"],
67
+ characteristics: ["tense", "unstable", "dissonant", "dominant function"],
68
+ pattern: ["major third", "major third", "minor second"],
69
+ patternShort: ["M3", "M3", "m2"],
70
+ } as const;
71
+
72
+ export const _augmented = {
73
+ augmentedTriad,
74
+ italian6,
75
+ french6,
76
+ german6,
77
+ augmented7,
78
+ } as const;
79
+
80
+ export type AugmentedKey = keyof typeof _augmented;
81
+
82
+ export const augmented: Record<AugmentedKey, NoteCollection> = _augmented;
@@ -0,0 +1,73 @@
1
+ import type { NoteCollection } from "../../types/note-collections.d.ts";
2
+
3
+ const diminishedTriad: NoteCollection = {
4
+ primaryName: "dim",
5
+ names: ["dim", "°", "Diminished Triad"],
6
+ intervals: ["1", "♭3", "♭5"],
7
+ integers: [0, 3, 6],
8
+ type: ["diminished", "chord", "arpeggio", "triad"],
9
+ characteristics: ["tense", "unstable", "dissonant"],
10
+ pattern: ["minor third", "minor third"],
11
+ patternShort: ["m3", "m3"],
12
+ } as const;
13
+
14
+ const diminished7: NoteCollection = {
15
+ primaryName: "dim7",
16
+ names: ["dim7", "°7", "Diminished 7th"],
17
+ intervals: ["1", "♭3", "♭5", "𝄫7"],
18
+ integers: [0, 3, 6, 9],
19
+ type: ["diminished", "chord", "arpeggio", "tetrad", "symmetrical"],
20
+ characteristics: ["very tense", "unstable", "symmetrical", "passing chord"],
21
+ pattern: ["minor third", "minor third", "minor third"],
22
+ patternShort: ["m3", "m3", "m3"],
23
+ } as const;
24
+
25
+ const halfDiminished7: NoteCollection = {
26
+ primaryName: "m7♭5",
27
+ names: ["m7♭5", "ø7", "Half Diminished 7th"],
28
+ intervals: ["1", "♭3", "♭5", "♭7"],
29
+ integers: [0, 3, 6, 10],
30
+ type: ["diminished", "minor", "chord", "arpeggio", "tetrad"],
31
+ characteristics: ["tense", "jazzy", "leading to minor"],
32
+ pattern: ["minor third", "minor third", "major third"],
33
+ patternShort: ["m3", "m3", "M3"],
34
+ } as const;
35
+
36
+ const wholeHalfDiminished: NoteCollection = {
37
+ primaryName: "Whole Half Diminished",
38
+ names: ["Whole Half Diminished"],
39
+ intervals: ["1", "2", "♭3", "4", "♭5", "♭6", "6", "7"],
40
+ integers: [0, 2, 3, 5, 6, 8, 9, 11],
41
+ type: ["diminished", "scale", "symmetrical", "octatonic"],
42
+ characteristics: ["tense", "jazzy", "symmetrical", "alternating tones"],
43
+ pattern: ["whole", "half", "whole", "half", "whole", "half", "whole", "half"],
44
+ patternShort: ["W", "H", "W", "H", "W", "H", "W", "H"],
45
+ } as const;
46
+
47
+ const halfWholeDiminished: NoteCollection = {
48
+ primaryName: "Half Whole Diminished",
49
+ names: ["Half Whole Diminished", "Dominant Diminished"],
50
+ intervals: ["1", "♭2", "♭3", "3", "♯4", "5", "6", "♭7"],
51
+ integers: [0, 1, 3, 4, 6, 7, 9, 10],
52
+ type: ["diminished", "dominant", "scale", "symmetrical", "octatonic"],
53
+ characteristics: [
54
+ "tense",
55
+ "jazzy",
56
+ "symmetrical",
57
+ "used over dominant 7th chords",
58
+ ],
59
+ pattern: ["half", "whole", "half", "whole", "half", "whole", "half", "whole"],
60
+ patternShort: ["H", "W", "H", "W", "H", "W", "H", "W"],
61
+ } as const;
62
+
63
+ export const _diminished = {
64
+ diminishedTriad,
65
+ diminished7,
66
+ halfDiminished7,
67
+ wholeHalfDiminished,
68
+ halfWholeDiminished,
69
+ } as const;
70
+
71
+ export type DiminishedKey = keyof typeof _diminished;
72
+
73
+ export const diminished: Record<DiminishedKey, NoteCollection> = _diminished;
@@ -0,0 +1,101 @@
1
+ import type { NoteCollection } from "../../types/note-collections.d.ts";
2
+
3
+ const minor: NoteCollection = {
4
+ primaryName: "m",
5
+ names: ["m", "min", "Minor", "Minor Triad"],
6
+ intervals: ["1", "♭3", "5"],
7
+ integers: [0, 3, 7],
8
+ type: ["minor", "chord", "arpeggio", "triad"],
9
+ characteristics: [
10
+ "sad",
11
+ "melancholic",
12
+ "dark",
13
+ "the most basic minor chord",
14
+ ],
15
+ pattern: ["minor third", "major third"],
16
+ patternShort: ["m3", "M3"],
17
+ } as const;
18
+
19
+ const minor6: NoteCollection = {
20
+ primaryName: "m6",
21
+ names: ["m6", "min6", "Minor 6th", "Minor Sixth"],
22
+ intervals: ["1", "♭3", "5", "6"],
23
+ integers: [0, 3, 7, 9],
24
+ type: ["minor", "chord", "arpeggio", "tetrad"],
25
+ characteristics: [
26
+ "jazzy",
27
+ "soulful",
28
+ "less dissonant than m7",
29
+ "dorian feel",
30
+ ],
31
+ pattern: ["minor third", "major third", "major second"],
32
+ patternShort: ["m3", "M3", "M2"],
33
+ } as const;
34
+
35
+ const minor7: NoteCollection = {
36
+ primaryName: "m7",
37
+ names: ["m7", "min7", "Minor 7th", "Minor Seventh"],
38
+ intervals: ["1", "♭3", "5", "♭7"],
39
+ integers: [0, 3, 7, 10],
40
+ type: ["minor", "chord", "arpeggio", "tetrad"],
41
+ characteristics: ["smooth", "jazzy", "versatile", "foundational minor chord"],
42
+ pattern: ["minor third", "major third", "minor third"],
43
+ patternShort: ["m3", "M3", "m3"],
44
+ } as const;
45
+
46
+ const minor9: NoteCollection = {
47
+ primaryName: "m9",
48
+ names: ["m9", "min9", "Minor 9th", "Minor Ninth"],
49
+ intervals: ["1", "♭3", "5", "♭7", "9"],
50
+ integers: [0, 2, 3, 7, 10],
51
+ type: ["minor", "chord", "arpeggio", "pentad"],
52
+ characteristics: ["rich", "lush", "sophisticated", "common in jazz"],
53
+ pattern: ["minor third", "major third", "minor third", "major third"],
54
+ patternShort: ["m3", "M3", "m3", "M3"],
55
+ } as const;
56
+
57
+ const minorAdd9: NoteCollection = {
58
+ primaryName: "m(add9)",
59
+ names: ["m(add9)", "min(add9)", "Minor add 9"],
60
+ intervals: ["1", "♭3", "5", "9"],
61
+ integers: [0, 2, 3, 7],
62
+ type: ["minor", "chord", "arpeggio", "tetrad"],
63
+ characteristics: [
64
+ "open",
65
+ "modern",
66
+ "adds color without the 7th",
67
+ "pop and rock music",
68
+ ],
69
+ pattern: ["minor third", "major third", "perfect fifth"],
70
+ patternShort: ["m3", "M3", "P5"],
71
+ } as const;
72
+
73
+ const minor6Add9: NoteCollection = {
74
+ primaryName: "m6/9",
75
+ names: ["m6/9", "min6/9", "Minor 6/9"],
76
+ intervals: ["1", "♭3", "5", "6", "9"],
77
+ integers: [0, 2, 3, 7, 9],
78
+ type: ["minor", "chord", "arpeggio", "pentad"],
79
+ characteristics: [
80
+ "rich",
81
+ "jazzy",
82
+ "dorian flavor",
83
+ "sophisticated minor sound",
84
+ ],
85
+ pattern: ["minor third", "major third", "major second", "perfect fourth"],
86
+ patternShort: ["m3", "M3", "M2", "P4"],
87
+ } as const;
88
+
89
+ export const _minorVariants = {
90
+ minor,
91
+ minor6,
92
+ minor7,
93
+ minor9,
94
+ minorAdd9,
95
+ minor6Add9,
96
+ } as const;
97
+
98
+ export type MinorVariantKey = keyof typeof _minorVariants;
99
+
100
+ export const minorVariants: Record<MinorVariantKey, NoteCollection> =
101
+ _minorVariants;
@@ -1,23 +1,35 @@
1
1
  import { diatonicModes } from "./diatonic-modes.ts";
2
2
  import { harmonicMinorModes } from "./harmonic-minor-modes.ts";
3
3
  import { melodicMinorModes } from "./melodic-minor-modes.ts";
4
+ import { minorVariants } from "./minor-variants.ts";
4
5
  import { dominantVariants } from "./dominant-variants.ts";
5
6
  import { majorVariants } from "./major-variants.ts";
7
+ import { pentatonics } from "./pentatonics.ts";
8
+ import { diminished } from "./diminished.ts";
9
+ import { augmented } from "./augmented.ts";
6
10
  import { otherNoteCollections } from "./other-collections.ts";
7
11
 
8
12
  export { diatonicModes } from "./diatonic-modes.ts";
9
13
  export { dominantVariants } from "./dominant-variants.ts";
10
14
  export { harmonicMinorModes } from "./harmonic-minor-modes.ts";
11
15
  export { majorVariants } from "./major-variants.ts";
16
+ export { minorVariants } from "./minor-variants.ts";
12
17
  export { melodicMinorModes } from "./melodic-minor-modes.ts";
18
+ export { pentatonics } from "./pentatonics.ts";
19
+ export { diminished } from "./diminished.ts";
20
+ export { augmented } from "./augmented.ts";
13
21
  export { otherNoteCollections } from "./other-collections.ts";
14
22
 
15
23
  export const noteCollections = {
16
24
  ...diatonicModes,
17
25
  ...harmonicMinorModes,
18
26
  ...melodicMinorModes,
19
- ...dominantVariants,
20
27
  ...majorVariants,
28
+ ...minorVariants,
29
+ ...dominantVariants,
30
+ ...pentatonics,
31
+ ...diminished,
32
+ ...augmented,
21
33
  ...otherNoteCollections,
22
34
  } as const;
23
35
 
@@ -27,8 +39,12 @@ export const groupedNoteCollections = {
27
39
  diatonicModes,
28
40
  harmonicMinorModes,
29
41
  melodicMinorModes,
30
- dominantVariants,
31
42
  majorVariants,
43
+ minorVariants,
44
+ dominantVariants,
45
+ pentatonics,
46
+ diminished,
47
+ augmented,
32
48
  otherNoteCollections,
33
49
  } as const;
34
50
 
@@ -56,15 +72,33 @@ export const noteCollectionGroupsMetadata: Record<
56
72
  description:
57
73
  "Seven-note scales derived from the melodic minor scale, each starting on a different scale degree.",
58
74
  },
75
+ majorVariants: {
76
+ displayName: "Major Variants",
77
+ description:
78
+ "Chord structures based on the major triad, including sixth and major seventh harmonies.",
79
+ },
80
+ minorVariants: {
81
+ displayName: "Minor Variants",
82
+ description:
83
+ "Chord structures based on the minor triad, including sixth and seventh harmonies.",
84
+ },
59
85
  dominantVariants: {
60
86
  displayName: "Dominant Variants",
61
87
  description:
62
88
  "Chord structures based on the dominant seventh chord, including extended harmonies (9ths, 11ths, 13ths).",
63
89
  },
64
- majorVariants: {
65
- displayName: "Major Variants",
90
+ pentatonics: {
91
+ displayName: "Pentatonic Scales",
92
+ description: "Five-note scales used widely in folk, blues, and rock music.",
93
+ },
94
+ diminished: {
95
+ displayName: "Diminished",
96
+ description: "Tense and dissonant chords and scales built on minor thirds.",
97
+ },
98
+ augmented: {
99
+ displayName: "Augmented",
66
100
  description:
67
- "Chord structures based on the major triad, including sixth and major seventh harmonies.",
101
+ "Unstable and dreamy chords and scales, including classical augmented sixth chords.",
68
102
  },
69
103
  otherNoteCollections: {
70
104
  displayName: "Other",
@@ -0,0 +1,93 @@
1
+ import type { NoteCollection } from "../../types/note-collections.d.ts";
2
+
3
+ const majorPentatonic: NoteCollection = {
4
+ primaryName: "Major Pentatonic",
5
+ names: ["Major Pentatonic"],
6
+ intervals: ["1", "2", "3", "5", "6"],
7
+ integers: [0, 2, 4, 7, 9],
8
+ type: ["major", "pentatonic", "scale", "gapped scale"],
9
+ characteristics: [
10
+ "open",
11
+ "positive",
12
+ "simple",
13
+ "found in many cultures",
14
+ "folk music",
15
+ "country music",
16
+ ],
17
+ pattern: ["whole", "whole", "minor third", "whole"],
18
+ patternShort: ["W", "W", "m3", "W"],
19
+ } as const;
20
+
21
+ const suspendedPentatonic: NoteCollection = {
22
+ primaryName: "Suspended Pentatonic",
23
+ names: ["Suspended Pentatonic", "Egyptian Pentatonic"],
24
+ intervals: ["1", "2", "4", "5", "♭7"],
25
+ integers: [0, 2, 5, 7, 10],
26
+ type: ["suspended", "pentatonic", "scale", "gapped scale"],
27
+ characteristics: ["open", "stable", "neutral", "neither major nor minor"],
28
+ pattern: ["whole", "minor third", "whole", "minor third"],
29
+ patternShort: ["W", "m3", "W", "m3"],
30
+ } as const;
31
+
32
+ const bluesMinorPentatonic: NoteCollection = {
33
+ primaryName: "Blues Minor Pentatonic",
34
+ names: ["Blues Minor Pentatonic"],
35
+ intervals: ["1", "♭3", "4", "♭5", "♭7"],
36
+ integers: [0, 3, 5, 6, 10],
37
+ type: ["minor", "pentatonic", "scale", "gapped scale", "blues"],
38
+ characteristics: ["bluesy", "tense", "minor with a blue note"],
39
+ pattern: ["minor third", "whole", "half", "minor third"],
40
+ patternShort: ["m3", "W", "H", "m3"],
41
+ } as const;
42
+
43
+ const bluesMajorPentatonic: NoteCollection = {
44
+ primaryName: "Blues Major Pentatonic",
45
+ names: ["Blues Major Pentatonic"],
46
+ intervals: ["1", "2", "♭3", "5", "6"],
47
+ integers: [0, 2, 3, 7, 9],
48
+ type: ["major", "pentatonic", "scale", "gapped scale", "blues"],
49
+ characteristics: ["bluesy", "country", "major with a blue note"],
50
+ pattern: ["whole", "half", "major third", "whole"],
51
+ patternShort: ["W", "H", "M3", "W"],
52
+ } as const;
53
+
54
+ const minorPentatonic: NoteCollection = {
55
+ primaryName: "Minor Pentatonic",
56
+ names: ["Minor Pentatonic"],
57
+ intervals: ["1", "♭3", "4", "5", "♭7"],
58
+ integers: [0, 3, 5, 7, 10],
59
+ type: ["minor", "pentatonic", "scale", "gapped scale"],
60
+ characteristics: [
61
+ "bluesy",
62
+ "rock",
63
+ "versatile",
64
+ "found in many cultures",
65
+ "relative of major pentatonic",
66
+ ],
67
+ pattern: ["minor third", "whole", "whole", "minor third"],
68
+ patternShort: ["m3", "W", "W", "m3"],
69
+ } as const;
70
+
71
+ const dominantPentatonic: NoteCollection = {
72
+ primaryName: "Dominant Pentatonic",
73
+ names: ["Dominant Pentatonic"],
74
+ intervals: ["1", "2", "3", "5", "♭7"],
75
+ integers: [0, 2, 4, 7, 10],
76
+ type: ["dominant", "pentatonic", "scale", "gapped scale"],
77
+ characteristics: ["bluesy", "dominant feel", "mixolydian flavor"],
78
+ pattern: ["whole", "whole", "minor third", "minor third"],
79
+ patternShort: ["W", "W", "m3", "m3"],
80
+ } as const;
81
+
82
+ export const _pentatonics = {
83
+ majorPentatonic,
84
+ suspendedPentatonic,
85
+ bluesMinorPentatonic,
86
+ bluesMajorPentatonic,
87
+ minorPentatonic,
88
+ dominantPentatonic,
89
+ } as const;
90
+
91
+ export type PentatonicKey = keyof typeof _pentatonics;
92
+
93
+ export const pentatonics: Record<PentatonicKey, NoteCollection> = _pentatonics;
@@ -2,45 +2,27 @@
2
2
  export default [
3
3
  {
4
4
  name: "m / Minor",
5
- category: "Minor",
6
5
  sequence: [0, 3, 7],
7
6
  },
8
7
  {
9
8
  name: "m6 / Minor Major 6th",
10
- category: "Minor",
11
9
  sequence: [0, 3, 7, 9],
12
10
  },
13
11
  {
14
12
  name: "m7 / Minor 7th",
15
- category: "Minor",
16
13
  sequence: [0, 3, 7, 10],
17
14
  },
18
15
  {
19
16
  name: "m9 / Minor 9th",
20
- category: "Minor",
21
17
  sequence: [0, 2, 3, 7, 10],
22
- labels: {
23
- Quality: { 2: "M9" },
24
- Relative: { 2: "9" },
25
- },
26
18
  },
27
19
  {
28
20
  name: "m(add9) / Minor add 9",
29
- category: "Minor",
30
21
  sequence: [0, 2, 3, 7],
31
- labels: {
32
- Quality: { 2: "M9" },
33
- Relative: { 2: "9" },
34
- },
35
22
  },
36
23
  {
37
24
  name: "m6/9 / Minor 6/9",
38
- category: "Minor",
39
25
  sequence: [0, 2, 3, 7, 9],
40
- labels: {
41
- Quality: { 2: "M9" },
42
- Relative: { 2: "9" },
43
- },
44
26
  },
45
27
  ];
46
28
 
@@ -49,188 +31,75 @@ export default [
49
31
  export default [
50
32
  {
51
33
  name: "Major Pentatonic",
52
- category: "Pentatonic",
53
34
  sequence: [0, 2, 4, 7, 9],
54
35
  },
55
36
  {
56
37
  name: "Suspended Pentatonic",
57
- category: "Pentatonic",
58
38
  sequence: [0, 2, 5, 7, 10],
59
39
  },
60
40
  {
61
41
  name: "Blues Minor Pentatonic",
62
- category: "Pentatonic",
63
42
  sequence: [0, 3, 5, 8, 10],
64
43
  },
65
44
  {
66
45
  name: "Blues Major Pentatonic",
67
- category: "Pentatonic",
68
46
  sequence: [0, 2, 5, 7, 9],
69
47
  },
70
48
  {
71
49
  name: "Minor Pentatonic",
72
- category: "Pentatonic",
73
50
  sequence: [0, 3, 5, 7, 10],
74
51
  },
75
52
  {
76
53
  name: "Dominant Pentatonic",
77
- category: "Pentatonic",
78
54
  sequence: [0, 2, 4, 7, 10],
79
55
  },
80
56
  ];
81
57
 
82
58
 
83
- // OTHER
84
- export default [
85
59
  // Diminished
86
60
  {
87
61
  name: "dim / o / Diminished Triad",
88
- category: "Diminished",
89
62
  sequence: [0, 3, 6],
90
63
  },
91
64
  {
92
65
  name: "dim7 / o7 / Diminished 7th",
93
- category: "Diminished",
94
66
  sequence: [0, 3, 6, 9],
95
- labels: {
96
- Quality: { 9: "d7" },
97
- Relative: { 9: "♭♭7" },
98
- Extension: { 9: "♭♭7" },
99
- },
100
67
  },
101
68
  {
102
69
  name: "m7♭5 / ø7 / Half Diminished 7th",
103
- category: "Diminished",
104
70
  sequence: [0, 3, 6, 10],
105
71
  },
106
72
  {
107
73
  name: "Whole Half Diminished",
108
- category: "Diminished",
109
74
  sequence: [0, 2, 3, 5, 6, 8, 9, 11],
110
- labels: {
111
- Quality: { 8: "A5" },
112
- Relative: { 8: "♯5" },
113
- Extension: { 8: "♯5" },
114
- },
115
75
  },
116
76
  {
117
77
  name: "Half Whole / Dominant Diminished",
118
- category: "Diminished",
119
78
  sequence: [0, 1, 3, 4, 6, 7, 9, 10],
120
- labels: {
121
- Quality: { 3: "A2", 6: "A4" },
122
- Relative: { 3: "♯2", 6: "♯4" },
123
- Extension: { 3: "♯9", 6: "♯11" },
124
- },
125
79
  },
80
+
126
81
  // Augmented
127
82
  {
128
83
  name: "aug / + / Augmented Triad",
129
- category: "Augmented",
130
84
  sequence: [0, 4, 8],
131
- labels: {
132
- Quality: { 8: "A5" },
133
- Relative: { 8: "♯5" },
134
- Extension: { 8: "♯5" },
135
- },
136
85
  },
137
86
  {
138
87
  name: "It+6 / Italian 6th",
139
- category: "Augmented",
140
88
  sequence: [0, 4, 10],
141
- labels: {
142
- Quality: { 10: "A6" },
143
- Relative: { 10: "♯6" },
144
- Extension: { 10: "♯13" },
145
- },
146
89
  },
147
90
  {
148
91
  name: "Fr+6 / French 6th",
149
- category: "Augmented",
150
92
  sequence: [0, 4, 6, 10],
151
- labels: {
152
- Quality: { 10: "A6" },
153
- Relative: { 10: "♯6" },
154
- Extension: { 10: "♯13" },
155
- },
156
93
  },
157
94
  {
158
95
  name: "Ger+6 / German 6th",
159
- category: "Augmented",
160
96
  sequence: [0, 4, 7, 10],
161
- labels: {
162
- Quality: { 10: "A6" },
163
- Relative: { 10: "♯6" },
164
- Extension: { 10: "♯13" },
165
- },
166
97
  },
167
98
  {
168
99
  name: "aug7 / +7 / 7♯5 / Augmented Seventh",
169
- category: "Augmented",
170
100
  sequence: [0, 4, 8, 10],
171
- labels: {
172
- Quality: { 8: "A5" },
173
- Relative: { 8: "♯5" },
174
- Extension: { 8: "♯5" },
175
- },
176
101
  },
177
102
 
178
- // Harmonic Minor
179
- {
180
- name: "Harmonic Minor",
181
- category: "Harmonic Minor",
182
- sequence: [0, 2, 3, 5, 7, 8, 11],
183
- },
184
- {
185
- name: "Locrian ♯6",
186
- category: "Harmonic Minor",
187
- sequence: [0, 1, 3, 5, 6, 9, 10],
188
- },
189
- {
190
- name: "Ionian ♯5",
191
- category: "Harmonic Minor",
192
- sequence: [0, 2, 4, 5, 8, 9, 11],
193
- labels: {
194
- Quality: { 8: "A5" },
195
- Relative: { 8: "♯5" },
196
- Extension: { 8: "♯5" },
197
- },
198
- },
199
- {
200
- name: "Dorian ♯4",
201
- category: "Harmonic Minor",
202
- sequence: [0, 2, 3, 6, 7, 9, 10],
203
- labels: {
204
- Quality: { 6: "A4" },
205
- Relative: { 6: "♯4" },
206
- Extension: { 6: "♯11" },
207
- },
208
- },
209
- {
210
- name: "Phrygian Dominant",
211
- category: "Harmonic Minor",
212
- sequence: [0, 1, 4, 5, 7, 8, 10],
213
- },
214
- {
215
- name: "Lydian ♯2",
216
- category: "Harmonic Minor",
217
- sequence: [0, 3, 4, 6, 7, 9, 11],
218
- labels: {
219
- Quality: { 3: "A2", 6: "A4" },
220
- Relative: { 3: "♯2", 6: "♯4" },
221
- Extension: { 3: "♯9", 6: "♯11" },
222
- },
223
- },
224
- {
225
- name: "Super Locrian ♭♭7",
226
- category: "Harmonic Minor",
227
- sequence: [0, 1, 3, 4, 6, 8, 9],
228
- labels: {
229
- Quality: { 4: "d4", 9: "d7" },
230
- Relative: { 4: "♭4", 9: "♭♭7" },
231
- Extension: { 4: "♭11", 9: "♭♭7" },
232
- },
233
- },
234
103
  // Other
235
104
  {
236
105
  name: "R / Root",
@@ -109,18 +109,37 @@ export function searchNoteCollections(
109
109
  }
110
110
 
111
111
  // 2. Apply prioritized text search on the filtered candidates
112
- const prioritizedResults = new Set<NoteCollection>();
113
112
  const normalizedQuery = normalizeSearchTerm(query);
114
113
 
115
114
  if (!normalizedQuery) {
116
115
  return candidates;
117
116
  }
118
117
 
119
- // Use case-sensitive search for "M" and "m" to avoid incorrect matches.
120
- const isCaseSensitiveQuery = normalizedQuery === "M" ||
121
- normalizedQuery === "m";
122
- const regexFlags = isCaseSensitiveQuery ? "" : "i";
123
- const searchRegex = new RegExp(`\\b${normalizedQuery}\\b`, regexFlags);
118
+ const searchWords = normalizedQuery.split(" ");
119
+
120
+ // Filter candidates to those that contain all search words
121
+ const textFilteredCandidates = candidates.filter((theme) => {
122
+ const searchableText = [
123
+ theme.primaryName,
124
+ ...theme.names,
125
+ ...theme.type,
126
+ ...theme.characteristics,
127
+ ]
128
+ .map(normalizeSearchTerm)
129
+ .join(" ");
130
+
131
+ return searchWords.every((word) => {
132
+ const isCaseSensitiveWord = word === "M" || word === "m";
133
+ const regex = new RegExp(
134
+ `\\b${word}\\b`,
135
+ isCaseSensitiveWord ? "" : "i",
136
+ );
137
+ return regex.test(searchableText);
138
+ });
139
+ });
140
+
141
+ // 3. Prioritize the filtered results
142
+ const prioritizedResults = new Set<NoteCollection>();
124
143
 
125
144
  const passes = [
126
145
  // Pass 1: Exact match on primaryName
@@ -129,30 +148,29 @@ export function searchNoteCollections(
129
148
  // Pass 2: Exact match on any name
130
149
  (theme: NoteCollection) =>
131
150
  theme.names.some((name) => normalizeSearchTerm(name) === normalizedQuery),
132
- // Pass 3: Whole word match on primaryName
151
+ // Pass 3: Primary name starts with the query
133
152
  (theme: NoteCollection) =>
134
- searchRegex.test(normalizeSearchTerm(theme.primaryName)),
135
- // Pass 4: Whole word match on any name
153
+ normalizeSearchTerm(theme.primaryName).startsWith(normalizedQuery),
154
+ // Pass 4: Any name starts with the query
136
155
  (theme: NoteCollection) =>
137
- theme.names.some((name) => searchRegex.test(normalizeSearchTerm(name))),
138
- // Pass 7: Whole word match on any type
139
- (theme: NoteCollection) =>
140
- theme.type.some((t) => searchRegex.test(normalizeSearchTerm(t))),
141
- // Pass 8: Whole word match on any characteristic
142
- (theme: NoteCollection) =>
143
- theme.characteristics.some((c) =>
144
- searchRegex.test(normalizeSearchTerm(c))
156
+ theme.names.some((name) =>
157
+ normalizeSearchTerm(name).startsWith(normalizedQuery)
145
158
  ),
146
159
  ];
147
160
 
148
161
  for (const pass of passes) {
149
- for (const theme of candidates) {
162
+ for (const theme of textFilteredCandidates) {
150
163
  if (pass(theme)) {
151
164
  prioritizedResults.add(theme);
152
165
  }
153
166
  }
154
167
  }
155
168
 
169
+ // Add the remaining text-filtered candidates that didn't match a priority pass
170
+ for (const theme of textFilteredCandidates) {
171
+ prioritizedResults.add(theme);
172
+ }
173
+
156
174
  return Array.from(prioritizedResults);
157
175
  }
158
176