- parseAstLog: abort on depth jump >1 or missing slot (depth>0) - buildStemNote: full clause tree (notes/pauses/stacks), editable fields - buildClause: replaces buildCluster; filters nested clause.chain silently - buildMotif: builds from voice.motif; sets isStatic - buildOffset: uses buildStemNote; drops old clusters/chains arrays - buildVoice: motifs are now objects, not label strings - PaneSubObjects: static motifs before offsets; motif/offset→stem_note drill-down - PaneFO: motif + stem_note FO cases; unknownProps display helper - PaneCP: motif + stem_note shortView cases - test-parser.mjs: 21 new synthetic tests for motif/clause/stack/guards
106 lines
4.8 KiB
JavaScript
106 lines
4.8 KiB
JavaScript
import { h } from 'vue';
|
|
import { ObjectShort } from './ObjectShort.js';
|
|
|
|
export const PaneSubObjects = {
|
|
props: ['store', 'onFocusFO'],
|
|
setup(props) {
|
|
function focused() {
|
|
const fp = props.store.focusPath;
|
|
return fp.length ? fp[fp.length - 1] : props.store.scoreModel;
|
|
}
|
|
|
|
function subItems(node) {
|
|
if (!node) return [];
|
|
if (node.type === 'score') {
|
|
const items = [];
|
|
if (node.info)
|
|
items.push({ kind: 'info', node: node.info, label: node.info.title ?? '(no title)', hasChildren: false });
|
|
if (node.tuning)
|
|
items.push({ kind: 'tuning', node: node.tuning, label: `base ${node.tuning.base ?? '?'}`, hasChildren: false });
|
|
for (const a of (node.articles ?? []))
|
|
items.push({ kind: 'article', node: a, label: a.name, hasChildren: false });
|
|
for (const sv of (node.stageVoices ?? []))
|
|
items.push({ kind: 'stage', node: sv, label: sv.name, hasChildren: false });
|
|
for (const i of node.instruments)
|
|
items.push({ kind: 'instrument', node: i, label: i.name, hasChildren: true });
|
|
for (const b of node.bars)
|
|
items.push({ kind: 'bar', node: b, label: b.id, hasChildren: Object.keys(b.voices).length > 0 });
|
|
return items;
|
|
}
|
|
if (node.type === 'instrument') {
|
|
return node.variations.map((v, idx) => ({
|
|
kind: 'variation',
|
|
node: v,
|
|
label: `variation ${idx + 1}${v.dependsOn ? ` (${v.dependsOn})` : ''}`,
|
|
hasChildren: true,
|
|
}));
|
|
}
|
|
if (node.type === 'variation') {
|
|
return [
|
|
...node.labelSpecs.map(ls => ({
|
|
kind: 'label_spec', node: ls, label: ls.label ?? '(no label)', hasChildren: false,
|
|
})),
|
|
...node.subvariations.map((sv, idx) => {
|
|
const dep = sv.dependsOn;
|
|
const label = dep == null ? `subvariation ${idx + 1}`
|
|
: isNaN(Number(dep)) ? `ATTR: ${dep}`
|
|
: String(dep);
|
|
return { kind: 'variation', node: sv, label, hasChildren: true };
|
|
}),
|
|
];
|
|
}
|
|
if (node.type === 'bar') {
|
|
return Object.entries(node.voices).map(([name, v]) => ({
|
|
kind: 'voice', node: v, label: name,
|
|
hasChildren: v.offsets.length > 0 || v.motifs.some(m => m.isStatic),
|
|
}));
|
|
}
|
|
if (node.type === 'voice') {
|
|
return [
|
|
...node.motifs
|
|
.filter(m => m.isStatic)
|
|
.map(m => ({ kind: 'motif', node: m, label: m.label, hasChildren: m.stemNotes.length > 0 })),
|
|
...node.offsets.map((o, idx) => ({
|
|
kind: 'offset', node: o, label: `tick ${o.tick ?? idx}`, hasChildren: o.stemNotes.length > 0,
|
|
})),
|
|
];
|
|
}
|
|
if (node.type === 'motif') {
|
|
return node.stemNotes.map(sn => ({
|
|
kind: 'stem_note', node: sn,
|
|
label: `pitch ${sn.pitch}${sn.clauses.length ? ` (${sn.clauses.length} clause${sn.clauses.length > 1 ? 's' : ''})` : ''}`,
|
|
hasChildren: false,
|
|
}));
|
|
}
|
|
if (node.type === 'offset') {
|
|
return node.stemNotes.map(sn => ({
|
|
kind: 'stem_note', node: sn,
|
|
label: `pitch ${sn.pitch}${sn.clauses.length ? ` (${sn.clauses.length} clause${sn.clauses.length > 1 ? 's' : ''})` : ''}`,
|
|
hasChildren: false,
|
|
}));
|
|
}
|
|
return [];
|
|
}
|
|
|
|
return () => {
|
|
const node = focused();
|
|
const items = subItems(node);
|
|
if (!items.length) return h('div', null, h('em', null, 'No sub-objects'));
|
|
|
|
return h('div', null,
|
|
h('ul', { class: 'se-object-list' }, items.map((item, idx) =>
|
|
h(ObjectShort, {
|
|
key: idx,
|
|
label: item.label,
|
|
typeTag: item.kind,
|
|
focused: props.store.focusPath.includes(item.node),
|
|
hasChildren: item.hasChildren,
|
|
onFocus: () => { props.store.pushFocus(item.node); props.onFocusFO?.(); },
|
|
onDrillDown: () => { props.store.pushFocus(item.node); props.onFocusFO?.(); },
|
|
})
|
|
))
|
|
);
|
|
};
|
|
},
|
|
};
|