Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions screen.js
Original file line number Diff line number Diff line change
Expand Up @@ -11639,6 +11639,9 @@ function _tempoSyncInspectorStatePure(measures, selectedIndex) {
numeratorValue: '',
denominatorValue: '4',
signatureDisabled: true,
canInsert: rows.length > 0,
canDelete: false,
deleteTitle: 'Select an interior sync point to delete it',
hint: 'Select a sync point on the Tempo Map grid.',
};
}
Expand All @@ -11647,6 +11650,8 @@ function _tempoSyncInspectorStatePure(measures, selectedIndex) {
? Number(selected.denominator)
: 4;
const hasBpm = !selected.isLast && Number(selected.bpm) > 0;
const selectedOrdinal = rows.indexOf(selected);
const canDelete = selectedOrdinal > 0 && selectedOrdinal < rows.length - 1;
return {
label: `Measure ${selected.measure}`,
bpmValue: hasBpm ? Number(selected.bpm).toFixed(2) : '',
Expand All @@ -11657,6 +11662,11 @@ function _tempoSyncInspectorStatePure(measures, selectedIndex) {
numeratorValue: String(numerator),
denominatorValue: String(denominator),
signatureDisabled: false,
canInsert: true,
canDelete,
deleteTitle: canDelete
? 'Delete selected sync point'
: 'First and final sync points cannot be deleted',
hint: hasBpm
? `${numerator}/${denominator}`
: `${numerator}/${denominator} - final measure BPM needs a closing downbeat.`,
Expand All @@ -11674,7 +11684,13 @@ function _ensureTempoSyncInspector() {
el.className = 'hidden items-center gap-1.5 px-2 py-0.5 rounded border border-gray-700 bg-dark-700/60 text-xs';
el.innerHTML = '<span class="text-gray-500">Sync point:</span>'
+ '<span id="editor-tempo-sync-label" class="text-gray-200 font-medium min-w-[5.5rem]">No selection</span>'
+ '<span id="editor-tempo-sync-hint" class="text-gray-500"></span>';
+ '<span id="editor-tempo-sync-hint" class="text-gray-500"></span>'
+ '<button type="button" id="editor-tempo-sync-insert" class="px-2 py-0.5 rounded bg-dark-600 text-gray-300 hover:bg-dark-500 disabled:opacity-50 disabled:cursor-not-allowed" title="Insert a sync point at the playhead">Insert</button>'
+ '<button type="button" id="editor-tempo-sync-delete" class="px-2 py-0.5 rounded bg-dark-600 text-gray-300 hover:bg-dark-500 disabled:opacity-50 disabled:cursor-not-allowed" title="Delete selected sync point">Delete</button>';
const insertBtn = el.querySelector('#editor-tempo-sync-insert');
const deleteBtn = el.querySelector('#editor-tempo-sync-delete');
if (insertBtn) insertBtn.onclick = () => _tempoInsertSyncPoint(S.cursorTime);
if (deleteBtn) deleteBtn.onclick = () => { if (S.tempoSel >= 0) _tempoDeleteSyncPoint(S.tempoSel); };
bpm.parentNode.insertBefore(el, bpm.previousElementSibling || bpm);
return el;
}
Expand All @@ -11685,11 +11701,13 @@ function _refreshTempoSyncInspector() {
const bpmEl = document.getElementById('editor-bpm');
const numEl = document.getElementById('editor-tempo-sig');
const denEl = document.getElementById('editor-tempo-sig-den');
const insertBtn = document.getElementById('editor-tempo-sync-insert');
const deleteBtn = document.getElementById('editor-tempo-sync-delete');
if (!el) return;
const hasGrid = !!(S.beats && S.beats.length >= 2);
const visible = !!S.tempoMapMode && hasGrid;
const state = _tempoSyncInspectorStatePure(visible ? _tempoMeasures() : [], visible ? S.tempoSel : -1);
const sig = `${visible}|${S.tempoSel}|${state.label}|${state.bpmValue}|${state.bpmDisabled}|${state.numeratorValue}|${state.denominatorValue}|${state.signatureDisabled}|${state.hint}`;
const sig = `${visible}|${S.tempoSel}|${state.label}|${state.bpmValue}|${state.bpmDisabled}|${state.numeratorValue}|${state.denominatorValue}|${state.signatureDisabled}|${state.canInsert}|${state.canDelete}|${state.hint}`;
_tempoSyncInspectorState = sig;
Comment on lines 11709 to 11711
el.classList.toggle('hidden', !visible);
el.classList.toggle('inline-flex', visible);
Expand Down Expand Up @@ -11734,6 +11752,14 @@ function _refreshTempoSyncInspector() {
denEl.style.opacity = '';
}
}
if (insertBtn && visible) {
insertBtn.disabled = !state.canInsert;
insertBtn.title = 'Insert a sync point at the playhead';
}
if (deleteBtn && visible) {
deleteBtn.disabled = !state.canDelete;
deleteBtn.title = state.deleteTitle;
}
}
function _ensureTempoSignatureControl() {
let wrap = document.getElementById('editor-tempo-sig-wrap');
Expand Down
8 changes: 8 additions & 0 deletions tests/tempo_sync_inspector.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ t('asks for a selected sync point before enabling edits', () => {
assert.strictEqual(state.bpmDisabled, true);
assert.strictEqual(state.signatureDisabled, true);
assert.strictEqual(state.bpmValue, '');
assert.strictEqual(state.canInsert, true);
assert.strictEqual(state.canDelete, false);
});

t('shows editable BPM and signature for a selected non-final measure', () => {
Expand All @@ -49,6 +51,9 @@ t('shows editable BPM and signature for a selected non-final measure', () => {
assert.strictEqual(state.denominatorValue, '8');
assert.strictEqual(state.signatureDisabled, false);
assert.strictEqual(state.hint, '7/8');
assert.strictEqual(state.canInsert, true);
assert.strictEqual(state.canDelete, true);
assert.strictEqual(state.deleteTitle, 'Delete selected sync point');
});

t('keeps signature editable but disables BPM for the final measure', () => {
Expand All @@ -59,6 +64,9 @@ t('keeps signature editable but disables BPM for the final measure', () => {
assert.strictEqual(state.signatureDisabled, false);
assert.ok(state.bpmTitle.includes('closing downbeat'));
assert.ok(state.hint.includes('final measure BPM'));
assert.strictEqual(state.canInsert, true);
assert.strictEqual(state.canDelete, false);
assert.ok(state.deleteTitle.includes('cannot be deleted'));
});

console.log(`\n${pass} passed, ${fail} failed`);
Expand Down