Make bars navigable and editable with _meta export

Navigation:
- SubObjects: bar → voice (with chevron when offsets exist) → offsets
- PaneCP shortView handles voice and offset types

FO pane:
- Bar: editable beats_per_minute, stress_pattern (serialized as
  groups string), upper/lower stress bounds and tempo shape via
  ShapeEditor; sets bar.isDirty on change
- Voice: read-only name, articles, motifs, offset count
- Offset: read-only tick, stem notes and clusters as label strings
  (note re-serialization deferred)

Export:
- exporter.patchScore() now accepts optional bars[] parameter
- Splits rawScoreText by \n---\n; instruments patched in header,
  dirty bar _meta blocks regenerated, voice note lines kept verbatim
- ast-parser buildBar() adds isDirty:false
- PaneCP passes scoreModel.bars to patchScore
This commit is contained in:
c0dev0id
2026-06-24 13:25:23 +02:00
parent 338ea5be49
commit 9eb4add695
5 changed files with 151 additions and 10 deletions

View File

@@ -26,6 +26,10 @@ function shortView(node) {
return { typeTag: 'label', label: node.label ?? '(no label)', meta: [] };
case 'bar':
return { typeTag: 'bar', label: node.id, meta: [] };
case 'voice':
return { typeTag: 'voice', label: node.name, meta: [] };
case 'offset':
return { typeTag: 'tick', label: String(node.tick ?? '?'), meta: [] };
default:
return { typeTag: node.type, label: node.type, meta: [] };
}
@@ -43,7 +47,7 @@ export const PaneCP = {
try {
const raw = await fetchScoreText(props.store.credentials);
props.store.rawScoreText = raw;
const patched = patchScore(raw, props.store.scoreModel.instruments);
const patched = patchScore(raw, props.store.scoreModel.instruments, props.store.scoreModel.bars);
await putScoreText(patched, props.store.credentials);
props.store.synthesisStatus = { frozen: false, progress: 0 };
} catch (e) {