refactor: underscore-prefix all non-exported functions

This commit is contained in:
c0dev0id
2026-07-06 21:14:36 +02:00
parent 3beaa1d2ec
commit 884955195a
4 changed files with 120 additions and 120 deletions

View File

@@ -25,7 +25,7 @@ export function parseAstLog(text) {
slot = slotFull.slice(dotIdx + 1); slot = slotFull.slice(dotIdx + 1);
} }
const { positionals, props } = parseRest(rest); const { positionals, props } = _parseRest(rest);
const node = { slot, parentSlot, depth, positionals, props, children: [] }; const node = { slot, parentSlot, depth, positionals, props, children: [] };
@@ -46,7 +46,7 @@ export function parseAstLog(text) {
return root; return root;
} }
function parseRest(rest) { function _parseRest(rest) {
const positionals = []; const positionals = [];
const props = {}; const props = {};
let i = 0; let i = 0;
@@ -100,7 +100,7 @@ function parseRest(rest) {
// ── Second pass: build typed model ────────────────────────────────────────── // ── Second pass: build typed model ──────────────────────────────────────────
function collectUnknownProps(nodeProps, knownKeys) { function _collectUnknownProps(nodeProps, knownKeys) {
return Object.fromEntries(Object.entries(nodeProps).filter(([k]) => !knownKeys.has(k))); return Object.fromEntries(Object.entries(nodeProps).filter(([k]) => !knownKeys.has(k)));
} }
@@ -143,7 +143,7 @@ export function buildModel(rawTree) {
score.info = { ...node.props }; score.info = { ...node.props };
break; break;
case 'tuning': case 'tuning':
score.tuning = buildTuning(node); score.tuning = _buildTuning(node);
break; break;
case 'stage.cone': case 'stage.cone':
score.stageCone = { type: 'stage_cone', ...node.props }; score.stageCone = { type: 'stage_cone', ...node.props };
@@ -157,16 +157,16 @@ export function buildModel(rawTree) {
}); });
break; break;
case 'instrument': case 'instrument':
score.instruments.push(buildInstrument(node)); score.instruments.push(_buildInstrument(node));
break; break;
case 'bar': case 'bar':
score.bars.push(buildBar(node)); score.bars.push(_buildBar(node));
break; break;
default: default:
if (node.parentSlot === 'articles') { if (node.parentSlot === 'articles') {
mergeArticleEntry(score, node); _mergeArticleEntry(score, node);
} else { } else {
score[node.slot] = buildGeneric(node); score[node.slot] = _buildGeneric(node);
} }
} }
} }
@@ -178,7 +178,7 @@ export function buildModel(rawTree) {
// — e.g. `articles.defaults 'f'` and (future) `articles.overwrites 'f'`. // — e.g. `articles.defaults 'f'` and (future) `articles.overwrites 'f'`.
// We merge them into one entry per label whose properties carry the scope // We merge them into one entry per label whose properties carry the scope
// of their originating subtype, so the UI can toggle scope per property. // of their originating subtype, so the UI can toggle scope per property.
function mergeArticleEntry(score, node) { function _mergeArticleEntry(score, node) {
const name = node.positionals[0]; const name = node.positionals[0];
let entry = score.articles.find(a => a.name === name); let entry = score.articles.find(a => a.name === name);
if (!entry) { if (!entry) {
@@ -192,7 +192,7 @@ function mergeArticleEntry(score, node) {
return entry; return entry;
} }
function buildTuning(node) { function _buildTuning(node) {
const t = { type: 'tuning', base: node.props.base, scales: {}, chords: {}, frequencyFactors: null }; const t = { type: 'tuning', base: node.props.base, scales: {}, chords: {}, frequencyFactors: null };
for (const child of node.children) { for (const child of node.children) {
if (child.slot === 'scales') { if (child.slot === 'scales') {
@@ -209,7 +209,7 @@ function buildTuning(node) {
return t; return t;
} }
function buildInstrument(node) { function _buildInstrument(node) {
const instr = { const instr = {
type: 'instrument', type: 'instrument',
name: node.positionals[0], name: node.positionals[0],
@@ -228,16 +228,16 @@ function buildInstrument(node) {
for (const child of node.children) { for (const child of node.children) {
switch (child.parentSlot + '.' + child.slot) { switch (child.parentSlot + '.' + child.slot) {
case 'character.variation': case 'character.variation':
instr.variations.push(buildVariation(child)); instr.variations.push(_buildVariation(child));
break; break;
case 'character.basic_properties': case 'character.basic_properties':
instr.basicProperties = buildBasicProperties(child); instr.basicProperties = _buildBasicProperties(child);
break; break;
case 'VOLUMES.shape': case 'VOLUMES.shape':
instr.volumes = buildShape(child); instr.volumes = _buildShape(child);
break; break;
case 'TIMBRE.shape': case 'TIMBRE.shape':
instr.timbre = buildShape(child); instr.timbre = _buildShape(child);
break; break;
case 'FM.modulation': case 'FM.modulation':
instr.fmModulations.push({ ...child.props }); instr.fmModulations.push({ ...child.props });
@@ -246,14 +246,14 @@ function buildInstrument(node) {
instr.amModulations.push({ ...child.props }); instr.amModulations.push({ ...child.props });
break; break;
default: default:
instr.unknownSlots.push(buildGeneric(child)); instr.unknownSlots.push(_buildGeneric(child));
} }
} }
return instr; return instr;
} }
function buildVariation(node) { function _buildVariation(node) {
const v = { const v = {
type: 'variation', type: 'variation',
dependsOn: node.props.depends_on ?? node.props.for_value ?? null, dependsOn: node.props.depends_on ?? node.props.for_value ?? null,
@@ -269,29 +269,29 @@ function buildVariation(node) {
const key = (child.parentSlot ?? child.slot) + '.' + child.slot; const key = (child.parentSlot ?? child.slot) + '.' + child.slot;
switch (key) { switch (key) {
case 'variation.basic_properties': case 'variation.basic_properties':
v.basicProperties = buildBasicProperties(child); v.basicProperties = _buildBasicProperties(child);
break; break;
case 'variation.label_spec': case 'variation.label_spec':
v.labelSpecs.push(buildLabelSpec(child)); v.labelSpecs.push(_buildLabelSpec(child));
break; break;
case 'variation.subvariation': case 'variation.subvariation':
v.subvariations.push(buildVariation(child)); v.subvariations.push(_buildVariation(child));
break; break;
case 'variation.SPREAD': case 'variation.SPREAD':
v.spread = child.positionals; v.spread = child.positionals;
break; break;
case 'RAILSBACK_CURVE.shape': case 'RAILSBACK_CURVE.shape':
v.railsbackCurve = buildShape(child); v.railsbackCurve = _buildShape(child);
break; break;
default: default:
v.unknownSlots.push(buildGeneric(child)); v.unknownSlots.push(_buildGeneric(child));
} }
} }
return v; return v;
} }
function buildBasicProperties(node) { function _buildBasicProperties(node) {
const bp = { const bp = {
type: 'basic_properties', type: 'basic_properties',
A: null, S: null, R: null, A: null, S: null, R: null,
@@ -304,32 +304,32 @@ function buildBasicProperties(node) {
for (const child of node.children) { for (const child of node.children) {
const fqSlot = (child.parentSlot ?? '') + (child.parentSlot ? '.' : '') + child.slot; const fqSlot = (child.parentSlot ?? '') + (child.parentSlot ? '.' : '') + child.slot;
if (child.parentSlot === 'A' && child.slot === 'shape') { if (child.parentSlot === 'A' && child.slot === 'shape') {
bp.A = buildShape(child); bp.A = _buildShape(child);
} else if (child.parentSlot === 'S' && child.slot === 'shape') { } else if (child.parentSlot === 'S' && child.slot === 'shape') {
bp.S = buildShape(child); bp.S = _buildShape(child);
} else if (child.parentSlot === 'R' && child.slot === 'shape') { } else if (child.parentSlot === 'R' && child.slot === 'shape') {
bp.R = buildShape(child); bp.R = _buildShape(child);
} else if (child.parentSlot === 'variation' && child.slot === 'O') { } else if (child.parentSlot === 'variation' && child.slot === 'O') {
bp.oscillator = child.props.ref ?? child.positionals[0]; bp.oscillator = child.props.ref ?? child.positionals[0];
} else if (child.parentSlot === 'FM' && child.slot === 'modulation') { } else if (child.parentSlot === 'FM' && child.slot === 'modulation') {
const fm = { ...child.props }; const fm = { ...child.props };
const envChild = child.children.find(c => c.slot === 'shape'); const envChild = child.children.find(c => c.slot === 'shape');
if (envChild) fm.shape = buildShape(envChild); if (envChild) fm.shape = _buildShape(envChild);
bp.fmModulations.push(fm); bp.fmModulations.push(fm);
} else if (child.parentSlot === 'AM' && child.slot === 'modulation') { } else if (child.parentSlot === 'AM' && child.slot === 'modulation') {
const am = { ...child.props }; const am = { ...child.props };
const envChild = child.children.find(c => c.slot === 'shape'); const envChild = child.children.find(c => c.slot === 'shape');
if (envChild) am.shape = buildShape(envChild); if (envChild) am.shape = _buildShape(envChild);
bp.amModulations.push(am); bp.amModulations.push(am);
} else { } else {
bp.unknownSlots.push(buildGeneric(child)); bp.unknownSlots.push(_buildGeneric(child));
} }
} }
return bp; return bp;
} }
function buildLabelSpec(node) { function _buildLabelSpec(node) {
const ls = { const ls = {
type: 'label_spec', type: 'label_spec',
label: node.positionals[0], label: node.positionals[0],
@@ -340,7 +340,7 @@ function buildLabelSpec(node) {
const directBpChildren = []; const directBpChildren = [];
for (const child of node.children) { for (const child of node.children) {
if (child.parentSlot === 'variation' && child.slot === 'basic_properties') { if (child.parentSlot === 'variation' && child.slot === 'basic_properties') {
ls.basicProperties = buildBasicProperties(child); ls.basicProperties = _buildBasicProperties(child);
} else if ( } else if (
(child.slot === 'shape' && (child.parentSlot === 'A' || child.parentSlot === 'S' || child.parentSlot === 'R')) || (child.slot === 'shape' && (child.parentSlot === 'A' || child.parentSlot === 'S' || child.parentSlot === 'R')) ||
(child.parentSlot === 'variation' && child.slot === 'O') || (child.parentSlot === 'variation' && child.slot === 'O') ||
@@ -348,17 +348,17 @@ function buildLabelSpec(node) {
) { ) {
directBpChildren.push(child); directBpChildren.push(child);
} else { } else {
ls.unknownSlots.push(buildGeneric(child)); ls.unknownSlots.push(_buildGeneric(child));
} }
} }
if (!ls.basicProperties && directBpChildren.length > 0) { if (!ls.basicProperties && directBpChildren.length > 0) {
ls.basicProperties = buildBasicProperties({ children: directBpChildren }); ls.basicProperties = _buildBasicProperties({ children: directBpChildren });
} }
return ls; return ls;
} }
function buildShape(node) { function _buildShape(node) {
return { return {
type: 'shape', type: 'shape',
length: node.props.length, length: node.props.length,
@@ -374,7 +374,7 @@ function buildShape(node) {
}; };
} }
function buildBar(node) { function _buildBar(node) {
const bar = { const bar = {
type: 'bar', type: 'bar',
id: node.positionals[0] ?? '', id: node.positionals[0] ?? '',
@@ -393,35 +393,35 @@ function buildBar(node) {
const fqSlot = (child.parentSlot ?? '') + (child.parentSlot ? '.' : '') + child.slot; const fqSlot = (child.parentSlot ?? '') + (child.parentSlot ? '.' : '') + child.slot;
switch (fqSlot) { switch (fqSlot) {
case 'stress_pattern.stressor': case 'stress_pattern.stressor':
bar.stressor = buildStressor(child); bar.stressor = _buildStressor(child);
break; break;
case 'tempo.shape': case 'tempo.shape':
bar.tempoShape = buildShape(child); bar.tempoShape = _buildShape(child);
break; break;
case 'tempo.levels': case 'tempo.levels':
bar.tempoLevels = child.positionals[0]; bar.tempoLevels = child.positionals[0];
break; break;
case 'lower_stress_bound.shape': case 'lower_stress_bound.shape':
bar.lowerStressBound = buildShape(child); bar.lowerStressBound = _buildShape(child);
break; break;
case 'upper_stress_bound.shape': case 'upper_stress_bound.shape':
bar.upperStressBound = buildShape(child); bar.upperStressBound = _buildShape(child);
break; break;
case 'bar.tuning': case 'bar.tuning':
bar.tunings.push({ ...child.props }); bar.tunings.push({ ...child.props });
break; break;
case 'bar.voice': case 'bar.voice':
bar.voices[child.positionals[0]] = buildVoice(child); bar.voices[child.positionals[0]] = _buildVoice(child);
break; break;
default: default:
bar.unknownSlots.push(buildGeneric(child)); bar.unknownSlots.push(_buildGeneric(child));
} }
} }
return bar; return bar;
} }
function buildStressor(node) { function _buildStressor(node) {
const levels = []; const levels = [];
let currentGroup = []; let currentGroup = [];
for (const child of node.children) { for (const child of node.children) {
@@ -436,7 +436,7 @@ function buildStressor(node) {
return { type: 'stressor', groups: levels }; return { type: 'stressor', groups: levels };
} }
function buildVoice(node) { function _buildVoice(node) {
const voice = { const voice = {
type: 'voice', type: 'voice',
name: node.positionals[0], name: node.positionals[0],
@@ -449,13 +449,13 @@ function buildVoice(node) {
const fqSlot = (child.parentSlot ?? '') + (child.parentSlot ? '.' : '') + child.slot; const fqSlot = (child.parentSlot ?? '') + (child.parentSlot ? '.' : '') + child.slot;
switch (fqSlot) { switch (fqSlot) {
case 'voice.offset': case 'voice.offset':
voice.offsets.push(buildOffset(child)); voice.offsets.push(_buildOffset(child));
break; break;
case 'voice.article': case 'voice.article':
voice.articles.push(child.positionals[0]); voice.articles.push(child.positionals[0]);
break; break;
case 'voice.motif': case 'voice.motif':
voice.motifs.push(buildMotif(child)); voice.motifs.push(_buildMotif(child));
break; break;
default: default:
// ignore // ignore
@@ -465,24 +465,24 @@ function buildVoice(node) {
return voice; return voice;
} }
function buildOffset(node) { function _buildOffset(node) {
const offset = { const offset = {
type: 'offset', type: 'offset',
tick: node.props.tick, tick: node.props.tick,
stemNotes: [], stemNotes: [],
motifRefs: [], motifRefs: [],
unknownProps: collectUnknownProps(node.props, new Set(['tick'])), unknownProps: _collectUnknownProps(node.props, new Set(['tick'])),
}; };
for (const child of node.children) { for (const child of node.children) {
if (child.parentSlot === 'line' && child.slot === 'stem_note') if (child.parentSlot === 'line' && child.slot === 'stem_note')
offset.stemNotes.push(buildStemNote(child)); offset.stemNotes.push(_buildStemNote(child));
else if (child.parentSlot === 'line' && child.slot === 'motif') else if (child.parentSlot === 'line' && child.slot === 'motif')
offset.motifRefs.push({ label: child.positionals[0], chord: child.props.chord ?? null }); offset.motifRefs.push({ label: child.positionals[0], chord: child.props.chord ?? null });
} }
return offset; return offset;
} }
function buildStemNote(node) { function _buildStemNote(node) {
const KNOWN = new Set(['pitch', 'eff_length', 'adj_stress', 'adjacent']); const KNOWN = new Set(['pitch', 'eff_length', 'adj_stress', 'adjacent']);
const chainNode = node.children.find(c => c.parentSlot === 'stem_note' && c.slot === 'chain'); const chainNode = node.children.find(c => c.parentSlot === 'stem_note' && c.slot === 'chain');
const writeToNode = node.children.find(c => c.parentSlot === 'stem_note' && c.slot === 'write_to'); const writeToNode = node.children.find(c => c.parentSlot === 'stem_note' && c.slot === 'write_to');
@@ -498,13 +498,13 @@ function buildStemNote(node) {
writeToName: writeToNode?.positionals[0] ?? null, writeToName: writeToNode?.positionals[0] ?? null,
clauses: (chainNode?.children ?? []) clauses: (chainNode?.children ?? [])
.filter(c => c.parentSlot === 'chain' && c.slot === 'clause') .filter(c => c.parentSlot === 'chain' && c.slot === 'clause')
.map(buildClause), .map(_buildClause),
isDirty: false, isDirty: false,
unknownProps: collectUnknownProps(node.props, KNOWN), unknownProps: _collectUnknownProps(node.props, KNOWN),
}; };
} }
function buildClause(node) { function _buildClause(node) {
return { return {
type: 'clause', type: 'clause',
index: node.positionals[0] ?? 0, index: node.positionals[0] ?? 0,
@@ -518,28 +518,28 @@ function buildClause(node) {
}; };
} }
function buildMotif(node) { function _buildMotif(node) {
const m = { const m = {
type: 'motif', type: 'motif',
label: node.props.label, label: node.props.label,
stemNotes: [], stemNotes: [],
unknownProps: collectUnknownProps(node.props, new Set(['label'])), unknownProps: _collectUnknownProps(node.props, new Set(['label'])),
}; };
for (const child of node.children) { for (const child of node.children) {
if (child.parentSlot === 'line' && child.slot === 'stem_note') if (child.parentSlot === 'line' && child.slot === 'stem_note')
m.stemNotes.push(buildStemNote(child)); m.stemNotes.push(_buildStemNote(child));
} }
m.isStatic = m.stemNotes.length > 0 && !m.stemNotes.some(sn => Number.isInteger(sn.pitch)); m.isStatic = m.stemNotes.length > 0 && !m.stemNotes.some(sn => Number.isInteger(sn.pitch));
return m; return m;
} }
function buildGeneric(node) { function _buildGeneric(node) {
return { return {
type: node.slot, type: node.slot,
parentSlot: node.parentSlot, parentSlot: node.parentSlot,
depth: node.depth, depth: node.depth,
positionals: node.positionals, positionals: node.positionals,
props: node.props, props: node.props,
children: node.children.map(buildGeneric), children: node.children.map(_buildGeneric),
}; };
} }

View File

@@ -7,19 +7,19 @@ import { coerce, stressorToString } from '../util.js';
const H4 = { style: 'margin:0 0 0.5rem' }; const H4 = { style: 'margin:0 0 0.5rem' };
function unknownPropFields(node) { function _unknownPropFields(node) {
return Object.entries(node.unknownProps ?? {}) return Object.entries(node.unknownProps ?? {})
.map(([key, value]) => ({ key, value: String(value), editable: false })); .map(([key, value]) => ({ key, value: String(value), editable: false }));
} }
function parseStressor(str) { function _parseStressor(str) {
const groups = str.split(';').map(seg => const groups = str.split(';').map(seg =>
seg.split(',').map(n => parseInt(n, 10)).filter(n => !isNaN(n)) seg.split(',').map(n => parseInt(n, 10)).filter(n => !isNaN(n))
).filter(g => g.length > 0); ).filter(g => g.length > 0);
return groups.length ? { type: 'stressor', groups } : null; return groups.length ? { type: 'stressor', groups } : null;
} }
function scoreInfoFields(info) { function _scoreInfoFields(info) {
return [ return [
{ key: 'title', value: info?.title ?? '', editable: true }, { key: 'title', value: info?.title ?? '', editable: true },
{ key: 'composer', value: info?.composer ?? '', editable: true }, { key: 'composer', value: info?.composer ?? '', editable: true },
@@ -28,7 +28,7 @@ function scoreInfoFields(info) {
]; ];
} }
function instrFields(instr) { function _instrFields(instr) {
return [ return [
{ key: 'name', value: instr.name, editable: false }, { key: 'name', value: instr.name, editable: false },
{ key: 'linked', value: instr.isLinked, editable: false, type: 'boolean' }, { key: 'linked', value: instr.isLinked, editable: false, type: 'boolean' },
@@ -36,11 +36,11 @@ function instrFields(instr) {
]; ];
} }
function variationFields(v) { function _variationFields(v) {
return [{ key: 'depends_on', value: v.dependsOn ?? '—', editable: true }]; return [{ key: 'depends_on', value: v.dependsOn ?? '—', editable: true }];
} }
function shapeSection(label, shape, onChange) { function _shapeSection(label, shape, onChange) {
if (!shape) return null; if (!shape) return null;
return h('div', { style: 'margin-top:0.5rem' }, [ return h('div', { style: 'margin-top:0.5rem' }, [
h('strong', null, label), h('strong', null, label),
@@ -101,7 +101,7 @@ export const PaneFO = {
return h('div', { class: 'se-fo-pane' }, [ return h('div', { class: 'se-fo-pane' }, [
h('h4', H4, 'Score'), h('h4', H4, 'Score'),
h(ObjectExtended, { h(ObjectExtended, {
fields: scoreInfoFields(model.info), fields: _scoreInfoFields(model.info),
onChange: ({ key, value }) => { onChange: ({ key, value }) => {
if (!model.info) model.info = {}; if (!model.info) model.info = {};
model.info[key] = value; model.info[key] = value;
@@ -114,7 +114,7 @@ export const PaneFO = {
if (node.type === 'instrument') { if (node.type === 'instrument') {
children.push( children.push(
h('h4', H4, `Instrument: ${node.name}`), h('h4', H4, `Instrument: ${node.name}`),
h(ObjectExtended, { fields: instrFields(node), onChange: null }), h(ObjectExtended, { fields: _instrFields(node), onChange: null }),
); );
} else if (node.type === 'variation') { } else if (node.type === 'variation') {
const instr = props.store.scoreModel.instruments.find( const instr = props.store.scoreModel.instruments.find(
@@ -125,7 +125,7 @@ export const PaneFO = {
children.push( children.push(
h('h4', H4, 'Variation'), h('h4', H4, 'Variation'),
h(ObjectExtended, { fields: variationFields(node), onChange: ({ key, value }) => { h(ObjectExtended, { fields: _variationFields(node), onChange: ({ key, value }) => {
if (key === 'depends_on') { if (key === 'depends_on') {
const old = node.dependsOn; const old = node.dependsOn;
node.dependsOn = value; node.dependsOn = value;
@@ -223,13 +223,13 @@ export const PaneFO = {
onChange: ({ key, value }) => { onChange: ({ key, value }) => {
if (key === 'id') { node.id = value; markBarDirty(); return; } if (key === 'id') { node.id = value; markBarDirty(); return; }
if (key === 'beats_per_minute') node.tempoLevels = isNaN(value) ? null : value; if (key === 'beats_per_minute') node.tempoLevels = isNaN(value) ? null : value;
if (key === 'stress_pattern') node.stressor = parseStressor(value); if (key === 'stress_pattern') node.stressor = _parseStressor(value);
markBarDirty(); markBarDirty();
}, },
}), }),
shapeSection('Upper stress bound', node.upperStressBound, markBarDirty), _shapeSection('Upper stress bound', node.upperStressBound, markBarDirty),
shapeSection('Lower stress bound', node.lowerStressBound, markBarDirty), _shapeSection('Lower stress bound', node.lowerStressBound, markBarDirty),
shapeSection('Tempo shape', node.tempoShape, markBarDirty), _shapeSection('Tempo shape', node.tempoShape, markBarDirty),
); );
} else if (node.type === 'voice') { } else if (node.type === 'voice') {
children.push( children.push(
@@ -254,7 +254,7 @@ export const PaneFO = {
node.motifRefs?.length node.motifRefs?.length
? { key: 'motifRefs', value: node.motifRefs.map(m => m.chord ? `${m.label}(${m.chord})` : m.label).join(', '), editable: false } ? { key: 'motifRefs', value: node.motifRefs.map(m => m.chord ? `${m.label}(${m.chord})` : m.label).join(', '), editable: false }
: null, : null,
...unknownPropFields(node), ..._unknownPropFields(node),
].filter(Boolean), ].filter(Boolean),
onChange: null, onChange: null,
}), }),
@@ -269,7 +269,7 @@ export const PaneFO = {
{ key: 'label', value: node.label, editable: false }, { key: 'label', value: node.label, editable: false },
{ key: 'static', value: node.isStatic, editable: false, type: 'boolean' }, { key: 'static', value: node.isStatic, editable: false, type: 'boolean' },
{ key: 'stem notes', value: node.stemNotes.map(pitchLabel).join(', ') || '—', editable: false }, { key: 'stem notes', value: node.stemNotes.map(pitchLabel).join(', ') || '—', editable: false },
...unknownPropFields(node), ..._unknownPropFields(node),
], ],
onChange: null, onChange: null,
}), }),
@@ -291,7 +291,7 @@ export const PaneFO = {
{ key: 'adj_stress', value: node.adjStress != null ? String(node.adjStress) : '', editable: true, type: 'number' }, { key: 'adj_stress', value: node.adjStress != null ? String(node.adjStress) : '', editable: true, type: 'number' },
{ key: 'chain', value: node.chainText, editable: true }, { key: 'chain', value: node.chainText, editable: true },
{ key: 'clauses', value: String(node.clauses.length), editable: false }, { key: 'clauses', value: String(node.clauses.length), editable: false },
...unknownPropFields(node), ..._unknownPropFields(node),
], ],
onChange: ({ key, value }) => { onChange: ({ key, value }) => {
if (key === 'pitch') node.pitch = value; if (key === 'pitch') node.pitch = value;

View File

@@ -4,13 +4,13 @@ import { getKindGroups } from '../subobject-kinds.js';
// Strip the final maximal run of same-class characters (all-digits or all-non-digits) // Strip the final maximal run of same-class characters (all-digits or all-non-digits)
// from the ID tail. Everything before that run is the group key. // from the ID tail. Everything before that run is the group key.
function barGroupKey(id) { function _barGroupKey(id) {
const m = id.match(/(\d+|\D+)$/); const m = id.match(/(\d+|\D+)$/);
return m ? id.slice(0, m.index) : id; return m ? id.slice(0, m.index) : id;
} }
// Increment the trailing numeric run of an ID, preserving zero-padding width. // Increment the trailing numeric run of an ID, preserving zero-padding width.
function incrementId(id) { function _incrementId(id) {
const m = id.match(/(\d+)$/); const m = id.match(/(\d+)$/);
if (!m) return id + '1'; if (!m) return id + '1';
const num = parseInt(m[1], 10) + 1; const num = parseInt(m[1], 10) + 1;
@@ -70,10 +70,10 @@ export const PaneSubObjects = {
if (!model) return; if (!model) return;
let newId; let newId;
if (afterId) { if (afterId) {
newId = incrementId(afterId); newId = _incrementId(afterId);
} else { } else {
const liveBars = (model.bars ?? []).filter(b => !b.deleted); const liveBars = (model.bars ?? []).filter(b => !b.deleted);
newId = liveBars.length ? incrementId(liveBars[liveBars.length - 1].id) : 'bar001'; newId = liveBars.length ? _incrementId(liveBars[liveBars.length - 1].id) : 'bar001';
} }
const newBar = { const newBar = {
type: 'bar', id: newId, isDirty: true, isNew: true, type: 'bar', id: newId, isDirty: true, isNew: true,
@@ -103,7 +103,7 @@ export const PaneSubObjects = {
const barGroups = []; const barGroups = [];
const seen = new Map(); const seen = new Map();
for (const item of visibleItems) { for (const item of visibleItems) {
const key = barGroupKey(item.label); const key = _barGroupKey(item.label);
if (!seen.has(key)) { if (!seen.has(key)) {
const g = { key, items: [] }; const g = { key, items: [] };
barGroups.push(g); barGroups.push(g);

View File

@@ -5,7 +5,7 @@ import { stressorToString } from './util.js';
// Node = x "," y ["*" z] ["!"] // Node = x "," y ["*" z] ["!"]
// PREFIX+colon is the duration/resolution; optional START+semicolon follows. // PREFIX+colon is the duration/resolution; optional START+semicolon follows.
function serializeShape(shape) { function _serializeShape(shape) {
if (!shape) return null; if (!shape) return null;
const nodes = shape.coords.map(c => { const nodes = shape.coords.map(c => {
let s = `${c.x},${c.y}`; let s = `${c.x},${c.y}`;
@@ -22,10 +22,10 @@ function serializeShape(shape) {
// ── FM / AM modulation ───────────────────────────────────────────────────── // ── FM / AM modulation ─────────────────────────────────────────────────────
// RFC §3.2.1.1.6-7: FM = FREQUENCY ["f"/"F"] ["@" OSC] ["[" SHAPE "]"] ";" MOD ":" BASE // RFC §3.2.1.1.6-7: FM = FREQUENCY ["f"/"F"] ["@" OSC] ["[" SHAPE "]"] ";" MOD ":" BASE
function serializeModulation(m) { function _serializeModulation(m) {
let s = String(m.frequency ?? ''); let s = String(m.frequency ?? '');
if (m.oscillator) s += `@${m.oscillator}`; if (m.oscillator) s += `@${m.oscillator}`;
if (m.shape) s += `[${serializeShape(m.shape)}]`; if (m.shape) s += `[${_serializeShape(m.shape)}]`;
s += `;${m.mod_share ?? ''}:${m.base_share ?? ''}`; s += `;${m.mod_share ?? ''}:${m.base_share ?? ''}`;
if (m.init_phase != null) s += m.init_phase >= 0 ? `+${m.init_phase}` : String(m.init_phase); if (m.init_phase != null) s += m.init_phase >= 0 ? `+${m.init_phase}` : String(m.init_phase);
return s; return s;
@@ -35,18 +35,18 @@ function serializeModulation(m) {
// RFC §3.2.1.1: O, A, S, R, FM go directly in the variation MAPPING. // RFC §3.2.1.1: O, A, S, R, FM go directly in the variation MAPPING.
// Returns array of YAML lines at 0 indent. // Returns array of YAML lines at 0 indent.
function basicPropLines(bp) { function _basicPropLines(bp) {
if (!bp) return []; if (!bp) return [];
const lines = []; const lines = [];
if (bp.oscillator) lines.push(`O: ${bp.oscillator}`); if (bp.oscillator) lines.push(`O: ${bp.oscillator}`);
const a = serializeShape(bp.A); const a = _serializeShape(bp.A);
if (a) lines.push(`A: "${a}"`); if (a) lines.push(`A: "${a}"`);
const s = serializeShape(bp.S); const s = _serializeShape(bp.S);
if (s) lines.push(`S: "${s}"`); if (s) lines.push(`S: "${s}"`);
const r = serializeShape(bp.R); const r = _serializeShape(bp.R);
if (r) lines.push(`R: "${r}"`); if (r) lines.push(`R: "${r}"`);
for (const fm of (bp.fmModulations ?? [])) lines.push(`FM: "${serializeModulation(fm)}"`); for (const fm of (bp.fmModulations ?? [])) lines.push(`FM: "${_serializeModulation(fm)}"`);
for (const am of (bp.amModulations ?? [])) lines.push(`AM: "${serializeModulation(am)}"`); for (const am of (bp.amModulations ?? [])) lines.push(`AM: "${_serializeModulation(am)}"`);
return lines; return lines;
} }
@@ -54,8 +54,8 @@ function basicPropLines(bp) {
// RFC §3.2.1.2: label name (3+ lowercase chars) is the MAPPING KEY directly. // RFC §3.2.1.2: label name (3+ lowercase chars) is the MAPPING KEY directly.
// Returns array of YAML lines at 0 indent. // Returns array of YAML lines at 0 indent.
function labelSpecLines(ls) { function _labelSpecLines(ls) {
const inner = basicPropLines(ls.basicProperties); const inner = _basicPropLines(ls.basicProperties);
if (!inner.length) return [`${ls.label}:`]; if (!inner.length) return [`${ls.label}:`];
return [`${ls.label}:`, ...inner.map(l => ` ${l}`)]; return [`${ls.label}:`, ...inner.map(l => ` ${l}`)];
} }
@@ -64,20 +64,20 @@ function labelSpecLines(ls) {
// Returns YAML lines for one variation MAPPING (no leading "- "). // Returns YAML lines for one variation MAPPING (no leading "- ").
// RFC §3.2.1.3: VOLUMES, TIMBRE are variation properties, not instrument-level. // RFC §3.2.1.3: VOLUMES, TIMBRE are variation properties, not instrument-level.
function variationLines(v) { function _variationLines(v) {
const lines = []; const lines = [];
if (v.dependsOn) lines.push(`ATTR: ${v.dependsOn}`); if (v.dependsOn) lines.push(`ATTR: ${v.dependsOn}`);
lines.push(...basicPropLines(v.basicProperties)); lines.push(..._basicPropLines(v.basicProperties));
for (const ls of (v.labelSpecs ?? [])) lines.push(...labelSpecLines(ls)); for (const ls of (v.labelSpecs ?? [])) lines.push(..._labelSpecLines(ls));
if (v.spread?.length) lines.push(`SPREAD: [${v.spread.join(', ')}]`); if (v.spread?.length) lines.push(`SPREAD: [${v.spread.join(', ')}]`);
if (v.railsbackCurve) { const rc = serializeShape(v.railsbackCurve); if (rc) lines.push(`RAILSBACK_CURVE: "${rc}"`); } if (v.railsbackCurve) { const rc = _serializeShape(v.railsbackCurve); if (rc) lines.push(`RAILSBACK_CURVE: "${rc}"`); }
const vol = serializeShape(v.volumes); const vol = _serializeShape(v.volumes);
if (vol) lines.push(`VOLUMES: "${vol}"`); if (vol) lines.push(`VOLUMES: "${vol}"`);
const timbre = serializeShape(v.timbre); const timbre = _serializeShape(v.timbre);
if (timbre) lines.push(`TIMBRE: "${timbre}"`); if (timbre) lines.push(`TIMBRE: "${timbre}"`);
for (const fm of (v.fmModulations ?? [])) lines.push(`FM: "${serializeModulation(fm)}"`); for (const fm of (v.fmModulations ?? [])) lines.push(`FM: "${_serializeModulation(fm)}"`);
for (const am of (v.amModulations ?? [])) lines.push(`AM: "${serializeModulation(am)}"`); for (const am of (v.amModulations ?? [])) lines.push(`AM: "${_serializeModulation(am)}"`);
for (const sv of (v.subvariations ?? [])) lines.push(...variationLines(sv)); for (const sv of (v.subvariations ?? [])) lines.push(..._variationLines(sv));
return lines; return lines;
} }
@@ -87,7 +87,7 @@ function variationLines(v) {
// is implicit when no character: wrapper exists). Promote them into a synthetic // is implicit when no character: wrapper exists). Promote them into a synthetic
// root variation here so the export structure is RFC-correct. // root variation here so the export structure is RFC-correct.
function instrCharacterLines(instr) { function _instrCharacterLines(instr) {
const variations = instr.variations ?? []; const variations = instr.variations ?? [];
const hasRootProps = instr.basicProperties || instr.volumes || instr.timbre || const hasRootProps = instr.basicProperties || instr.volumes || instr.timbre ||
(instr.fmModulations ?? []).length > 0 || (instr.fmModulations ?? []).length > 0 ||
@@ -110,14 +110,14 @@ function instrCharacterLines(instr) {
]; ];
if (allVariations.length <= 1) { if (allVariations.length <= 1) {
const vLines = allVariations.length ? variationLines(allVariations[0]) : []; const vLines = allVariations.length ? _variationLines(allVariations[0]) : [];
return vLines.map(l => ` ${l}`); return vLines.map(l => ` ${l}`);
} }
// Multiple variations — RFC MAYBE_LIST<VARIATION> as YAML sequence. // Multiple variations — RFC MAYBE_LIST<VARIATION> as YAML sequence.
const result = []; const result = [];
for (const v of allVariations) { for (const v of allVariations) {
const vLines = variationLines(v); const vLines = _variationLines(v);
if (!vLines.length) continue; if (!vLines.length) continue;
result.push(` - ${vLines[0]}`); result.push(` - ${vLines[0]}`);
for (const l of vLines.slice(1)) result.push(` ${l}`); for (const l of vLines.slice(1)) result.push(` ${l}`);
@@ -132,33 +132,33 @@ export function exportInstrument(instr) {
const lines = [`instrument ${instr.name}:`]; const lines = [`instrument ${instr.name}:`];
if (instr.notChangedSince) lines.push(` NOT_CHANGED_SINCE: ${instr.notChangedSince}`); if (instr.notChangedSince) lines.push(` NOT_CHANGED_SINCE: ${instr.notChangedSince}`);
lines.push(` character:`); lines.push(` character:`);
lines.push(...instrCharacterLines(instr)); lines.push(..._instrCharacterLines(instr));
return lines.join('\n'); return lines.join('\n');
} }
// ── Articles ─────────────────────────────────────────────────────────────── // ── Articles ───────────────────────────────────────────────────────────────
// RFC §4.3: articles: MAPPING { LABEL: { ATTR: VALUE ... } ... } // RFC §4.3: articles: MAPPING { LABEL: { ATTR: VALUE ... } ... }
function serializeArticleValue(v) { function _serializeArticleValue(v) {
if (typeof v === 'boolean') return String(v); if (typeof v === 'boolean') return String(v);
if (typeof v === 'number') return String(v); if (typeof v === 'number') return String(v);
const s = String(v); const s = String(v);
return /[:#\[\]{}&*!,|>'"%@`]/.test(s) ? JSON.stringify(s) : s; return /[:#\[\]{}&*!,|>'"%@`]/.test(s) ? JSON.stringify(s) : s;
} }
function buildArticlesBlock(articles) { function _buildArticlesBlock(articles) {
const lines = ['articles:']; const lines = ['articles:'];
for (const art of articles) { for (const art of articles) {
const props = (art.properties ?? []).filter(p => p.name); const props = (art.properties ?? []).filter(p => p.name);
if (!props.length) continue; if (!props.length) continue;
lines.push(` ${art.name}:`); lines.push(` ${art.name}:`);
for (const p of props) lines.push(` ${p.name}: ${serializeArticleValue(p.value)}`); for (const p of props) lines.push(` ${p.name}: ${_serializeArticleValue(p.value)}`);
} }
return lines.length > 1 ? lines.join('\n') : null; return lines.length > 1 ? lines.join('\n') : null;
} }
function patchArticles(text, articles) { function _patchArticles(text, articles) {
const newBlock = buildArticlesBlock(articles); const newBlock = _buildArticlesBlock(articles);
if (!newBlock) return text; if (!newBlock) return text;
const lines = text.split('\n'); const lines = text.split('\n');
const artIdx = lines.findIndex(l => /^articles\s*:/.test(l)); const artIdx = lines.findIndex(l => /^articles\s*:/.test(l));
@@ -180,7 +180,7 @@ function patchArticles(text, articles) {
const META_KEYS = ['title', 'composer', 'source', 'encrypter']; const META_KEYS = ['title', 'composer', 'source', 'encrypter'];
function patchMetadata(text, info) { function _patchMetadata(text, info) {
if (!info) return text; if (!info) return text;
const lines = text.split('\n'); const lines = text.split('\n');
const replaced = new Set(); const replaced = new Set();
@@ -203,7 +203,7 @@ function patchMetadata(text, info) {
return out.join('\n'); return out.join('\n');
} }
function patchInstrumentHeader(text, instruments) { function _patchInstrumentHeader(text, instruments) {
const lines = text.split('\n'); const lines = text.split('\n');
const result = []; const result = [];
const instrMap = {}; const instrMap = {};
@@ -241,17 +241,17 @@ function patchInstrumentHeader(text, instruments) {
} }
function patchBarMeta(doc, bar) { function _patchBarMeta(doc, bar) {
const props = []; const props = [];
const sp = stressorToString(bar.stressor); const sp = stressorToString(bar.stressor);
if (sp) props.push(` stress_pattern: ${sp}`); if (sp) props.push(` stress_pattern: ${sp}`);
if (bar.tempoLevels != null) if (bar.tempoLevels != null)
props.push(` beats_per_minute: ${bar.tempoLevels}`); props.push(` beats_per_minute: ${bar.tempoLevels}`);
const ub = serializeShape(bar.upperStressBound); const ub = _serializeShape(bar.upperStressBound);
if (ub) props.push(` upper_stress_bound: ${ub}`); if (ub) props.push(` upper_stress_bound: ${ub}`);
const lb = serializeShape(bar.lowerStressBound); const lb = _serializeShape(bar.lowerStressBound);
if (lb) props.push(` lower_stress_bound: ${lb}`); if (lb) props.push(` lower_stress_bound: ${lb}`);
if (bar.tempoShape) { const ts = serializeShape(bar.tempoShape); if (ts) props.push(` tempo_shape: "${ts}"`); } if (bar.tempoShape) { const ts = _serializeShape(bar.tempoShape); if (ts) props.push(` tempo_shape: "${ts}"`); }
const lines = doc.split('\n'); const lines = doc.split('\n');
const out = []; const out = [];
@@ -278,7 +278,7 @@ function patchBarMeta(doc, bar) {
return out.join('\n'); return out.join('\n');
} }
function buildNewBarDoc(bar) { function _buildNewBarDoc(bar) {
const lines = [`_id: ${bar.id}`, '_meta:']; const lines = [`_id: ${bar.id}`, '_meta:'];
const sp = stressorToString(bar.stressor); const sp = stressorToString(bar.stressor);
if (sp) lines.push(` stress_pattern: ${sp}`); if (sp) lines.push(` stress_pattern: ${sp}`);
@@ -291,15 +291,15 @@ export function patchScore(rawScoreText, instruments, bars = [], info = null, ar
const SEP = '\n---\n'; const SEP = '\n---\n';
const [header, ...barDocs] = rawScoreText.split(SEP); const [header, ...barDocs] = rawScoreText.split(SEP);
let patchedHeader = patchMetadata(header, info); let patchedHeader = _patchMetadata(header, info);
const dirtyArticles = articles.filter(a => a.isDirty); const dirtyArticles = articles.filter(a => a.isDirty);
if (dirtyArticles.length || flags.articlesModified) { if (dirtyArticles.length || flags.articlesModified) {
patchedHeader = patchArticles(patchedHeader, articles); patchedHeader = _patchArticles(patchedHeader, articles);
for (const a of dirtyArticles) log.push({ level: 'changed', path: `articles / ${a.name}` }); for (const a of dirtyArticles) log.push({ level: 'changed', path: `articles / ${a.name}` });
} }
patchedHeader = patchInstrumentHeader(patchedHeader, instruments); patchedHeader = _patchInstrumentHeader(patchedHeader, instruments);
for (const i of instruments) { for (const i of instruments) {
if (i.deleted) log.push({ level: 'changed', path: `instrument / ${i.name} (deleted)` }); if (i.deleted) log.push({ level: 'changed', path: `instrument / ${i.name} (deleted)` });
else if (i.isDirty) log.push({ level: 'changed', path: `instrument / ${i.name}` }); else if (i.isDirty) log.push({ level: 'changed', path: `instrument / ${i.name}` });
@@ -309,7 +309,7 @@ export function patchScore(rawScoreText, instruments, bars = [], info = null, ar
for (const bar of bars) barMap[bar.id] = bar; for (const bar of bars) barMap[bar.id] = bar;
if (!barDocs.length) { if (!barDocs.length) {
const newBarDocs = bars.filter(b => b.isNew && !b.deleted).map(b => buildNewBarDoc(b)); const newBarDocs = bars.filter(b => b.isNew && !b.deleted).map(b => _buildNewBarDoc(b));
for (const b of bars.filter(b => b.isNew && !b.deleted)) log.push({ level: 'changed', path: `bar / ${b.id}` }); for (const b of bars.filter(b => b.isNew && !b.deleted)) log.push({ level: 'changed', path: `bar / ${b.id}` });
return { text: [patchedHeader, ...newBarDocs].join(SEP), log }; return { text: [patchedHeader, ...newBarDocs].join(SEP), log };
} }
@@ -324,11 +324,11 @@ export function patchScore(rawScoreText, instruments, bars = [], info = null, ar
if (bar.deleted) continue; if (bar.deleted) continue;
if (!bar.isDirty) { passedThrough++; patchedBarDocs.push(doc); continue; } if (!bar.isDirty) { passedThrough++; patchedBarDocs.push(doc); continue; }
log.push({ level: 'changed', path: `bar / ${bar.id}` }); log.push({ level: 'changed', path: `bar / ${bar.id}` });
patchedBarDocs.push(patchBarMeta(doc, bar)); patchedBarDocs.push(_patchBarMeta(doc, bar));
} }
for (const bar of bars.filter(b => b.isNew && !b.deleted)) { for (const bar of bars.filter(b => b.isNew && !b.deleted)) {
patchedBarDocs.push(buildNewBarDoc(bar)); patchedBarDocs.push(_buildNewBarDoc(bar));
log.push({ level: 'changed', path: `bar / ${bar.id}` }); log.push({ level: 'changed', path: `bar / ${bar.id}` });
} }