Simplify: remove dead code and redundancy across vue app

- Delete ObjectBasic (defined, never imported)
- ObjectShort: remove unused node prop
- exporter: export stressorToString; use in patchBarMeta (removes inline duplicate)
- PaneFO: import stressorToString from exporter; extract H4 style constant,
  shapeSection() helper, instrOnChange() helper; remove apply param from
  makeChangeHandler (was always () => markDirty()); remove unnecessary ?.
  on always-initialized arrays (variations, labelSpecs, subvariations)
- PaneSubObjects: explicit hasChildren on all item kinds; bars at score
  level get hasChildren based on voices; remove stale kind-list fallback
- ast-parser coerce(): remove unreachable s.trim() check ('' handled above)
- PaneCP: fix post-export synthesisStatus reset to use real field names
This commit is contained in:
c0dev0id
2026-06-24 16:08:52 +02:00
parent 9eb4add695
commit 1706404b7e
7 changed files with 80 additions and 103 deletions

View File

@@ -3,11 +3,9 @@ import { ObjectExtended } from './ObjectExtended.js';
import { EnvelopeEditor } from './EnvelopeEditor.js';
import { ShapeEditor } from './ShapeEditor.js';
import { LinkedInstrumentModal } from './LinkedInstrumentModal.js';
import { stressorToString } from '../exporter.js';
function stressorToString(s) {
if (!s?.groups?.length) return '';
return s.groups.map(g => g.join(',')).join(';');
}
const H4 = { style: 'margin:0 0 0.5rem' };
function parseStressor(str) {
const groups = str.split(';').map(seg =>
@@ -27,38 +25,42 @@ function scoreInfoFields(info) {
function instrFields(instr) {
return [
{ key: 'name', value: instr.name, editable: false },
{ key: 'linked', value: instr.isLinked, editable: false, type: 'boolean' },
{ key: 'name', value: instr.name, editable: false },
{ key: 'linked', value: instr.isLinked, editable: false, type: 'boolean' },
{ key: 'NOT_CHANGED_SINCE', value: instr.notChangedSince ?? '—', editable: false },
];
}
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) {
if (!shape) return null;
return h('div', { style: 'margin-top:0.5rem' }, [
h('strong', null, label),
h(ShapeEditor, { shape, onChange }),
]);
}
export const PaneFO = {
props: ['store'],
setup(props) {
// Pending edit held while the linked-instrument modal is shown.
const pendingEdit = ref(null); // { instr, apply: fn }
const pendingEdit = ref(null); // { instr, undo? }
function focused() {
const fp = props.store.focusPath;
return fp.length ? fp[fp.length - 1] : null;
}
// Returns a change handler that intercepts the first edit to a linked
// instrument and shows the embed-or-discard modal before applying it.
// `info` may carry { undo } forwarded from ShapeEditor / EnvelopeEditor.
function makeChangeHandler(instr, apply) {
// Intercepts the first edit to a linked instrument:
// shows embed-or-discard modal before committing. `info.undo`
// (forwarded from ShapeEditor/EnvelopeEditor) reverts the mutation on discard.
function makeChangeHandler(instr) {
return (info) => {
if (instr.isLinked && !instr.isDirty) {
pendingEdit.value = { instr, apply, undo: info?.undo };
pendingEdit.value = { instr, undo: info?.undo };
} else {
apply();
instr.isDirty = true;
props.store.markDirty();
}
@@ -69,7 +71,6 @@ export const PaneFO = {
instr.name = instr.name.split('/').pop();
instr.isLinked = false;
instr.isDirty = true;
pendingEdit.value.apply();
pendingEdit.value = null;
props.store.markDirty();
}
@@ -79,6 +80,12 @@ export const PaneFO = {
pendingEdit.value = null;
}
function instrOnChange(instr) {
return instr
? makeChangeHandler(instr)
: () => props.store.markDirty();
}
return () => {
const node = focused();
const children = [];
@@ -87,7 +94,7 @@ export const PaneFO = {
const model = props.store.scoreModel;
if (!model) return h('div', { class: 'se-fo-pane' }, 'No score loaded');
return h('div', { class: 'se-fo-pane' }, [
h('h4', { style: 'margin:0 0 0.5rem' }, 'Score'),
h('h4', H4, 'Score'),
h(ObjectExtended, {
fields: scoreInfoFields(model.info),
onChange: ({ key, value }) => {
@@ -101,22 +108,18 @@ export const PaneFO = {
if (node.type === 'instrument') {
children.push(
h('h4', { style: 'margin:0 0 0.5rem' }, `Instrument: ${node.name}`),
h('h4', H4, `Instrument: ${node.name}`),
h(ObjectExtended, { fields: instrFields(node), onChange: null }),
);
} else if (node.type === 'variation') {
// Find the ancestor instrument for linked-instrument gating.
const instr = props.store.scoreModel?.instruments.find(
i => i.variations?.includes(node) ||
i.variations?.some(v => v.subvariations?.includes(node))
const instr = props.store.scoreModel.instruments.find(
i => i.variations.includes(node) ||
i.variations.some(v => v.subvariations.includes(node))
) ?? null;
const onChange = instr
? makeChangeHandler(instr, () => { props.store.markDirty(); })
: () => props.store.markDirty();
const onChange = instrOnChange(instr);
children.push(
h('h4', { style: 'margin:0 0 0.5rem' }, 'Variation'),
h('h4', H4, 'Variation'),
h(ObjectExtended, { fields: variationFields(node), onChange: ({ key, value }) => {
if (key === 'depends_on') {
const old = node.dependsOn;
@@ -131,19 +134,16 @@ export const PaneFO = {
: null,
);
} else if (node.type === 'label_spec') {
const instr = props.store.scoreModel?.instruments.find(
i => i.variations?.some(v =>
v.labelSpecs?.includes(node) ||
v.subvariations?.some(sv => sv.labelSpecs?.includes(node))
const instr = props.store.scoreModel.instruments.find(
i => i.variations.some(v =>
v.labelSpecs.includes(node) ||
v.subvariations.some(sv => sv.labelSpecs.includes(node))
)
) ?? null;
const onChange = instr
? makeChangeHandler(instr, () => { props.store.markDirty(); })
: () => props.store.markDirty();
const onChange = instrOnChange(instr);
children.push(
h('h4', { style: 'margin:0 0 0.5rem' }, `Label: ${node.label}`),
h('h4', H4, `Label: ${node.label}`),
node.basicProperties
? h(EnvelopeEditor, { basicProperties: node.basicProperties, onChange })
: null,
@@ -151,35 +151,26 @@ export const PaneFO = {
} else if (node.type === 'bar') {
const markBarDirty = () => { node.isDirty = true; props.store.markDirty(); };
children.push(
h('h4', { style: 'margin:0 0 0.5rem' }, `Bar: ${node.id}`),
h('h4', H4, `Bar: ${node.id}`),
h(ObjectExtended, {
fields: [
{ key: 'id', value: node.id, editable: false },
{ key: 'beats_per_minute', value: node.tempoLevels ?? '', editable: true, type: 'number' },
{ key: 'stress_pattern', value: stressorToString(node.stressor), editable: true },
{ key: 'id', value: node.id, editable: false },
{ key: 'beats_per_minute', value: node.tempoLevels ?? '', editable: true, type: 'number' },
{ key: 'stress_pattern', value: stressorToString(node.stressor), editable: true },
],
onChange: ({ key, 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();
},
}),
node.upperStressBound ? h('div', { style: 'margin-top:0.5rem' }, [
h('strong', null, 'Upper stress bound'),
h(ShapeEditor, { shape: node.upperStressBound, onChange: markBarDirty }),
]) : null,
node.lowerStressBound ? h('div', { style: 'margin-top:0.5rem' }, [
h('strong', null, 'Lower stress bound'),
h(ShapeEditor, { shape: node.lowerStressBound, onChange: markBarDirty }),
]) : null,
node.tempoShape ? h('div', { style: 'margin-top:0.5rem' }, [
h('strong', null, 'Tempo shape'),
h(ShapeEditor, { shape: node.tempoShape, onChange: markBarDirty }),
]) : null,
shapeSection('Upper stress bound', node.upperStressBound, markBarDirty),
shapeSection('Lower stress bound', node.lowerStressBound, markBarDirty),
shapeSection('Tempo shape', node.tempoShape, markBarDirty),
);
} else if (node.type === 'voice') {
children.push(
h('h4', { style: 'margin:0 0 0.5rem' }, `Voice: ${node.name}`),
h('h4', H4, `Voice: ${node.name}`),
h(ObjectExtended, {
fields: [
{ key: 'articles', value: node.articles.join(', ') || '—', editable: false },
@@ -190,27 +181,26 @@ export const PaneFO = {
}),
);
} else if (node.type === 'offset') {
const noteStr = n => `${n.pitch}${n.effLength != null ? ' ' + n.effLength : ''}`;
const noteStr = n => `${n.pitch}${n.effLength != null ? ' ' + n.effLength : ''}`;
const clusterStr = c => c.notes.length
? c.notes.map(n => `${n.letter ?? ''}${n.shift != null ? n.shift : ''}${n.length != null ? ' ' + n.length : ''}`).join(', ')
: `cluster[${c.index}]`;
const noteItem = (text, i) => h('li', { class: 'se-object-item', key: i },
h('span', { class: 'se-object-label' }, text));
children.push(
h('h4', { style: 'margin:0 0 0.5rem' }, `Tick: ${node.tick}`),
h('h4', H4, `Tick: ${node.tick}`),
h(ObjectExtended, {
fields: [{ key: 'tick', value: node.tick, editable: false }],
onChange: null,
}),
node.stemNotes.length ? h('div', { style: 'margin-top:0.5rem' }, [
h('strong', null, 'Stem notes'),
h('ul', { class: 'se-object-list' }, node.stemNotes.map((n, i) =>
h('li', { class: 'se-object-item', key: i },
h('span', { class: 'se-object-label' }, noteStr(n))))),
h('ul', { class: 'se-object-list' }, node.stemNotes.map((n, i) => noteItem(noteStr(n), i))),
]) : null,
node.clusters.length ? h('div', { style: 'margin-top:0.5rem' }, [
h('strong', null, 'Clusters'),
h('ul', { class: 'se-object-list' }, node.clusters.map((c, i) =>
h('li', { class: 'se-object-item', key: i },
h('span', { class: 'se-object-label' }, clusterStr(c))))),
h('ul', { class: 'se-object-list' }, node.clusters.map((c, i) => noteItem(clusterStr(c), i))),
]) : null,
);
} else {
@@ -223,8 +213,8 @@ export const PaneFO = {
pendingEdit.value
? h(LinkedInstrumentModal, {
instrumentName: pendingEdit.value.instr.name,
onEmbed: () => embedInstrument(pendingEdit.value.instr),
onDiscard: discardEdit,
onEmbed: () => embedInstrument(pendingEdit.value.instr),
onDiscard: discardEdit,
})
: null,
]);