Articles: key by label across subtypes, scope per property, FO toggle widget
A single article label (e.g. 'f') may emit on multiple `articles.<subtype>`
lines — `defaults` today, `overwrites` planned — with disjoint property
sets that apply at different stages. Model now merges those emissions:
{ name: 'f', properties: [{ name, value, scope: 'defaults'|'overwrites' }] }
Per-label entries in the AR pane show a property-count breakdown
(`f (1 defaults, 2 overwrites)`). The Article FO pane lists each property
with a `(O-) default` / `(-O) overwrite` toggle — click flips the scope
and marks the score dirty. ASCII glyph mimics a physical switch position
so the active scope is visible at a glance.
Fixture tests cover both the existing single-subtype shape and a synthetic
two-subtype merge (177 assertions total).
This commit is contained in:
@@ -172,7 +172,7 @@ export function buildModel(rawTree) {
|
||||
break;
|
||||
default:
|
||||
if (node.parentSlot === 'articles') {
|
||||
score.articles.push(buildArticle(node));
|
||||
mergeArticleEntry(score, node);
|
||||
} else {
|
||||
score[node.slot] = buildGeneric(node);
|
||||
}
|
||||
@@ -182,6 +182,24 @@ export function buildModel(rawTree) {
|
||||
return score;
|
||||
}
|
||||
|
||||
// Articles are keyed by label. The AST emits one line per (label, subtype)
|
||||
// — e.g. `articles.defaults 'f'` and (future) `articles.overwrites 'f'`.
|
||||
// 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.
|
||||
function mergeArticleEntry(score, node) {
|
||||
const name = node.positionals[0];
|
||||
let entry = score.articles.find(a => a.name === name);
|
||||
if (!entry) {
|
||||
entry = { type: 'article', name, properties: [] };
|
||||
score.articles.push(entry);
|
||||
}
|
||||
const scope = node.slot; // 'defaults' | 'overwrites' | future subtype
|
||||
for (const [key, value] of Object.entries(node.props)) {
|
||||
entry.properties.push({ name: key, value, scope });
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
function buildTuning(node) {
|
||||
const t = { type: 'tuning', base: node.props.base, scales: {}, chords: {}, frequencyFactors: null };
|
||||
for (const child of node.children) {
|
||||
@@ -199,15 +217,6 @@ function buildTuning(node) {
|
||||
return t;
|
||||
}
|
||||
|
||||
function buildArticle(node) {
|
||||
return {
|
||||
type: 'article',
|
||||
subtype: node.slot,
|
||||
name: node.positionals[0],
|
||||
props: { ...node.props },
|
||||
};
|
||||
}
|
||||
|
||||
function buildInstrument(node) {
|
||||
const instr = {
|
||||
type: 'instrument',
|
||||
|
||||
Reference in New Issue
Block a user