phases 4-6: article system, stem note fields, edit-state cleanup
Phase 4 — remove edit-state flags from domain objects: - ast-parser.js: drop isDirty from buildInstrument, buildBar, buildStemNote - store.js: add resetEditState() - PaneCP.js: use resetEditState() on import; drop flags arg from patchScore - PaneFO.js: isDirty -> _modified for linked instruments; no per-node dirty tracking - PaneSubObjects.js: _spliceNode helper; deletion via splice; _isNew on new bars - exporter.js: on-demand traversal; absence from model means deleted Phase 5 — article parser and exporter: - ast-parser.js: _buildArticleEntry replaces _mergeArticleEntry; stage.article and voice.article dispatch; article properties carry scope and overwritten flag - exporter.js: _buildArticlesBlock emits -important: list for overwrites scope Phase 6 — stem note dead fields: - ast-parser.js: parse weight and articulatory on stem notes; flatten article.definite properties onto stem note model sibling to pitch/chain
This commit is contained in:
@@ -109,14 +109,24 @@ export function buildModel(rawTree) {
|
||||
case 'stage.cone':
|
||||
score.stageCone = { type: 'stage_cone', ...node.props };
|
||||
break;
|
||||
case 'stage.voice':
|
||||
score.stageVoices.push({
|
||||
case 'stage.article':
|
||||
score.articles.push(_buildArticleEntry(node));
|
||||
break;
|
||||
case 'stage.voice': {
|
||||
const sv = {
|
||||
type: 'stage_voice',
|
||||
name: node.positionals[0],
|
||||
direction: node.props.direction,
|
||||
distance: node.props.distance,
|
||||
});
|
||||
articles: [],
|
||||
};
|
||||
for (const child of node.children) {
|
||||
if (child.parentSlot === 'voice' && child.slot === 'article')
|
||||
sv.articles.push(_buildArticleEntry(child));
|
||||
}
|
||||
score.stageVoices.push(sv);
|
||||
break;
|
||||
}
|
||||
case 'instrument':
|
||||
score.instruments.push(_buildInstrument(node));
|
||||
break;
|
||||
@@ -124,31 +134,27 @@ export function buildModel(rawTree) {
|
||||
score.bars.push(_buildBar(node));
|
||||
break;
|
||||
default:
|
||||
if (node.parentSlot === 'articles') {
|
||||
_mergeArticleEntry(score, node);
|
||||
} else {
|
||||
score[node.slot] = _buildGeneric(node);
|
||||
}
|
||||
score[node.slot] = _buildGeneric(node);
|
||||
}
|
||||
}
|
||||
|
||||
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 });
|
||||
function _buildArticleEntry(node) {
|
||||
const entry = { type: 'article', name: node.positionals[0], properties: [] };
|
||||
for (const child of node.children) {
|
||||
if (child.parentSlot !== 'article') continue;
|
||||
if (child.slot === 'defaults' || child.slot === 'definite') {
|
||||
const raw = child.props.constant ?? child.props.stacked ?? child.props.static;
|
||||
const shapeChild = child.children.find(c => c.slot === 'shape');
|
||||
const value = raw !== undefined ? coerce(raw) : (shapeChild ? _buildShape(shapeChild) : null);
|
||||
entry.properties.push({ name: child.positionals[0], value, scope: child.slot, overwritten: child.props.overwritten ?? false });
|
||||
} else if (child.slot === 'overwrites') {
|
||||
for (const propName of child.positionals) {
|
||||
entry.properties.push({ name: propName, value: null, scope: 'overwrites' });
|
||||
}
|
||||
}
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
@@ -176,7 +182,6 @@ function _buildInstrument(node) {
|
||||
name: node.positionals[0],
|
||||
notChangedSince: node.props.NOT_CHANGED_SINCE ?? null,
|
||||
isLinked: (node.positionals[0] ?? '').includes('/'),
|
||||
isDirty: false,
|
||||
variations: [],
|
||||
basicProperties: null,
|
||||
volumes: null,
|
||||
@@ -339,7 +344,6 @@ function _buildBar(node) {
|
||||
const bar = {
|
||||
type: 'bar',
|
||||
id: node.positionals[0] ?? '',
|
||||
isDirty: false,
|
||||
stressor: null,
|
||||
tempoShape: null,
|
||||
tempoLevels: null,
|
||||
@@ -413,7 +417,7 @@ function _buildVoice(node) {
|
||||
voice.offsets.push(_buildOffset(child));
|
||||
break;
|
||||
case 'voice.article':
|
||||
voice.articles.push(child.positionals[0]);
|
||||
voice.articles.push(_buildArticleEntry(child));
|
||||
break;
|
||||
case 'voice.motif':
|
||||
voice.motifs.push(_buildMotif(child));
|
||||
@@ -444,9 +448,17 @@ function _buildOffset(node) {
|
||||
}
|
||||
|
||||
function _buildStemNote(node) {
|
||||
const KNOWN = new Set(['pitch', 'eff_length', 'adj_stress', 'adjacent']);
|
||||
const KNOWN = new Set(['pitch', 'eff_length', 'adj_stress', 'adjacent', 'weight', 'articulatory']);
|
||||
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 definiteProps = {};
|
||||
for (const child of node.children) {
|
||||
if (child.parentSlot === 'article' && child.slot === 'definite') {
|
||||
const raw = child.props.constant ?? child.props.stacked ?? child.props.static;
|
||||
const shapeChild = child.children.find(c => c.slot === 'shape');
|
||||
definiteProps[child.positionals[0]] = raw !== undefined ? coerce(raw) : (shapeChild ? _buildShape(shapeChild) : null);
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: 'stem_note',
|
||||
pitch: node.props.pitch,
|
||||
@@ -454,13 +466,13 @@ function _buildStemNote(node) {
|
||||
adjacent: node.props.adjacent ?? null,
|
||||
adjStress: node.props.adj_stress ?? null,
|
||||
length: null,
|
||||
weight: null,
|
||||
weight: node.props.weight ?? null,
|
||||
chainText: chainNode?.props._tmp_string ?? '',
|
||||
writeToName: writeToNode?.positionals[0] ?? null,
|
||||
clauses: (chainNode?.children ?? [])
|
||||
.filter(c => c.parentSlot === 'chain' && c.slot === 'clause')
|
||||
.map(_buildClause),
|
||||
isDirty: false,
|
||||
...definiteProps,
|
||||
unknownProps: _collectUnknownProps(node.props, KNOWN),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user