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
21 changes: 14 additions & 7 deletions screen.js
Original file line number Diff line number Diff line change
Expand Up @@ -10375,12 +10375,19 @@ function _tempoMapDraw(w, h) {
ctx.font = '11px sans-serif';
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
ctx.fillText(
`Tempo Map — ${measures.length} measures · drag a pole to retime · `
+ `right-click: insert/delete · [ ]: time signature`,
LABEL_W + 6, hudY);
ctx.fillText(_tempoMapHudTextPure(measures.length, w), LABEL_W + 6, hudY);
}

/* @pure:tempo-map-guidance:start */
function _tempoMapHudTextPure(measureCount, width) {
const n = Number.isFinite(Number(measureCount)) ? Number(measureCount) : 0;
if (Number(width) < 760) {
return `Tempo Map - ${n} measures - right-click sync point: BPM / signature`;
}
return `Tempo Map - ${n} measures - drag poles to retime - right-click sync point: BPM / signature/delete - right-click grid: insert`;
}
/* @pure:tempo-map-guidance:end */

function _ensureTempoSignatureControl() {
let wrap = document.getElementById('editor-tempo-sig-wrap');
if (wrap) return wrap;
Expand Down Expand Up @@ -10414,7 +10421,7 @@ function _ensureTempoMapButton() {
btn.type = 'button';
btn.textContent = '🎵 Tempo Map';
btn.className = 'px-3 py-1 bg-dark-600 hover:bg-dark-500 rounded text-xs font-medium hidden';
btn.title = 'Open the EOF-style tempo-map editor';
btn.title = 'Open Tempo Map to edit sync points, BPM, and time signatures';
btn.onclick = () => {
// Finalize any in-progress canvas drag before switching
// modes — commit a moved sync-point / drum drag (don't
Expand Down Expand Up @@ -10609,8 +10616,8 @@ function _tempoMapOnMouseDown(e, x, y) {
draw();
}

// Right-click in tempo-map mode: insert a sync point on open grid, or
// delete / change the time signature of the sync point under the cursor.
// Right-click in tempo-map mode: insert on open grid, or edit/delete
// the sync point under the cursor.
function _tempoMapOnContextMenu(e) {
const { x, y } = getMousePos(e);
const menu = document.getElementById('editor-context-menu');
Expand Down
50 changes: 50 additions & 0 deletions tests/tempo_map_guidance.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict';
/*
* Tempo-map guidance helper tests for screen.js.
*
* Run: node tests/tempo_map_guidance.test.js
*/
const fs = require('fs');
const path = require('path');
const assert = require('assert');

const src = fs.readFileSync(path.join(__dirname, '..', 'screen.js'), 'utf8');
const m = src.match(/\/\* @pure:tempo-map-guidance:start \*\/[\s\S]*?\/\* @pure:tempo-map-guidance:end \*\//);
if (!m) {
console.error('FAIL: @pure:tempo-map-guidance block not found in screen.js');
process.exit(1);
}

const api = new Function(
'"use strict";' + m[0] + '\nreturn { _tempoMapHudTextPure };'
)();

let pass = 0;
let fail = 0;
function t(name, fn) {
try { fn(); pass++; console.log(' ok ' + name); }
catch (e) { fail++; console.error(' FAIL ' + name + ': ' + e.message); }
}

t('uses compact guidance on narrow canvases', () => {
const text = api._tempoMapHudTextPure(12, 640);
assert.ok(text.includes('12 measures'));
assert.ok(text.includes('right-click sync point'));
assert.ok(text.includes('BPM / signature'));
assert.ok(!text.includes('right-click grid'));
});

t('uses full guidance when there is room', () => {
const text = api._tempoMapHudTextPure(24, 960);
assert.ok(text.includes('24 measures'));
assert.ok(text.includes('drag poles to retime'));
assert.ok(text.includes('BPM / signature/delete'));
assert.ok(text.includes('right-click grid: insert'));
});

t('normalizes invalid measure counts to zero', () => {
assert.ok(api._tempoMapHudTextPure('bad', 960).includes('0 measures'));
});

console.log(`\n${pass} passed, ${fail} failed`);
process.exit(fail ? 1 : 0);