mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-12 03:41:40 +00:00
feat(highway): render caged + guideTones teaching labels (§6.6) (#545)
Mirror the voicing/fn.rn teaching-mark render for the two new chord-template
fields, in both the 2D and 3D highways:
- Extend the shared pure chordHarmonyLabels() helper (identical in static/highway.js
and plugins/highway_3d/screen.js) to also surface caged ("CAGED: E") and
guideTones ("gt 4,10"), pre-formatted and node-testable. Invalid caged enum and
out-of-range / non-int guide tones are filtered out.
- Draw both, stacked above the existing rn/voicing labels, in distinct colors.
- Gated behind the SAME teaching-marks toggle (_showTeachingMarks 2D /
teachingMarksVisible 3D) — no clutter on the default highway.
Render only — no scoring / NoteVerifier coupling.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
4195b73877
commit
e518910baa
@@ -29,21 +29,42 @@ const labels2D = loadFn('static/highway.js', 'chordHarmonyLabels');
|
||||
const labels3D = loadFn('plugins/highway_3d/screen.js', 'chordHarmonyLabels');
|
||||
|
||||
for (const [name, fn] of [['2D', labels2D], ['3D', labels3D]]) {
|
||||
test(`chordHarmonyLabels (${name}) surfaces rn + voicing`, () => {
|
||||
assert.deepEqual(fn({ rn: 'ii7', q: 'm7', deg: 2 }, 'open'),
|
||||
{ rn: 'ii7', voicing: 'open' });
|
||||
test(`chordHarmonyLabels (${name}) surfaces rn + voicing + caged + guideTones`, () => {
|
||||
assert.deepEqual(fn({ rn: 'ii7', q: 'm7', deg: 2 }, 'open', 'E', [4, 10]),
|
||||
{ rn: 'ii7', voicing: 'open', caged: 'CAGED: E', guideTones: 'gt 4,10' });
|
||||
});
|
||||
|
||||
test(`chordHarmonyLabels (${name}) trims whitespace`, () => {
|
||||
assert.deepEqual(fn({ rn: ' V7 ' }, ' drop2 '),
|
||||
{ rn: 'V7', voicing: 'drop2' });
|
||||
assert.deepEqual(fn({ rn: ' V7 ' }, ' drop2 ', ' G ', []),
|
||||
{ rn: 'V7', voicing: 'drop2', caged: 'CAGED: G', guideTones: '' });
|
||||
});
|
||||
|
||||
test(`chordHarmonyLabels (${name}) empties absent / malformed inputs`, () => {
|
||||
assert.deepEqual(fn(null, undefined), { rn: '', voicing: '' });
|
||||
assert.deepEqual(fn({}, ''), { rn: '', voicing: '' });
|
||||
assert.deepEqual(fn({ rn: 7 }, 7), { rn: '', voicing: '' }); // non-string
|
||||
assert.deepEqual(fn(undefined, 'shell'), { rn: '', voicing: 'shell' });
|
||||
assert.deepEqual(fn({ rn: 'vi' }, null), { rn: 'vi', voicing: '' });
|
||||
assert.deepEqual(fn(null, undefined),
|
||||
{ rn: '', voicing: '', caged: '', guideTones: '' });
|
||||
assert.deepEqual(fn({}, ''),
|
||||
{ rn: '', voicing: '', caged: '', guideTones: '' });
|
||||
assert.deepEqual(fn({ rn: 7 }, 7), // non-string
|
||||
{ rn: '', voicing: '', caged: '', guideTones: '' });
|
||||
assert.deepEqual(fn(undefined, 'shell'),
|
||||
{ rn: '', voicing: 'shell', caged: '', guideTones: '' });
|
||||
assert.deepEqual(fn({ rn: 'vi' }, null),
|
||||
{ rn: 'vi', voicing: '', caged: '', guideTones: '' });
|
||||
});
|
||||
|
||||
test(`chordHarmonyLabels (${name}) rejects invalid caged enum`, () => {
|
||||
assert.equal(fn(null, null, 'X').caged, ''); // not a CAGED letter
|
||||
assert.equal(fn(null, null, 'e').caged, ''); // lower-case rejected
|
||||
assert.equal(fn(null, null, 7).caged, ''); // non-string
|
||||
assert.equal(fn(null, null, ['E']).caged, ''); // non-string
|
||||
assert.equal(fn(null, null, 'C').caged, 'CAGED: C');
|
||||
});
|
||||
|
||||
test(`chordHarmonyLabels (${name}) filters out-of-range / non-int guide tones`, () => {
|
||||
assert.equal(fn(null, null, '', [12, -1, 3, 'x', 10]).guideTones, 'gt 3,10');
|
||||
assert.equal(fn(null, null, '', [0, 11]).guideTones, 'gt 0,11'); // boundaries kept
|
||||
assert.equal(fn(null, null, '', []).guideTones, '');
|
||||
assert.equal(fn(null, null, '', '4,10').guideTones, ''); // non-array
|
||||
assert.equal(fn(null, null, '', [12, -1]).guideTones, ''); // all dropped
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user