diff --git a/static/app.js b/static/app.js index bda30e3..6c0ad2f 100644 --- a/static/app.js +++ b/static/app.js @@ -127,6 +127,7 @@ import { toggleSectionPracticePopover, } from './js/section-practice.js'; import { configureHost } from './js/host.js'; +import { formatTime } from './js/format.js'; // The playback transport. These used to BE app.js — they are imported back now, and the // four modules that reached for them through the host seam import them directly instead. import { @@ -4821,7 +4822,6 @@ if (window.feedBack) { } -function formatTime(s) { return `${Math.floor(s/60)}:${String(Math.floor(s%60)).padStart(2,'0')}`; } @@ -6261,7 +6261,6 @@ async function checkScanAndLoad() { // tests/js/host_contract.test.js fails CI if this list and the host.* uses under // static/js/ ever drift apart. configureHost({ - formatTime, handleSliderInput, playSong, // count-in is a module now, so section-practice reaches it through the seam too — diff --git a/static/js/format.js b/static/js/format.js new file mode 100644 index 0000000..1a902a1 --- /dev/null +++ b/static/js/format.js @@ -0,0 +1,17 @@ +// Display formatters. A LEAF module: imports nothing. +// +// WHY THIS EXISTS FOR ONE FUNCTION. formatTime was a HOST HOOK — loops.js and +// section-practice.js both reached back through the seam for it. It was also, by pure +// accident of who calls it, inside the dependency closure of the library carve. Leaving +// it there would have made loops.js and section-practice.js import the LIBRARY to format +// a timestamp, which is nonsense, and a cycle waiting to happen. +// +// A hook is a cycle you agreed to live with. This one has a real owner — it just isn't +// app.js, and it certainly isn't the library. Give it a home of its own and both +// consumers import it directly. +// +// It is a leaf on purpose. Anything else that turns out to be a shared pure formatter +// belongs here too; nothing does yet, so nothing else is here. + +/** Seconds -> `M:SS`. */ +export function formatTime(s) { return `${Math.floor(s / 60)}:${String(Math.floor(s % 60)).padStart(2, '0')}`; } diff --git a/static/js/loops.js b/static/js/loops.js index 312cd09..898f484 100644 --- a/static/js/loops.js +++ b/static/js/loops.js @@ -20,6 +20,7 @@ // fails CI if the hooks used here and the hooks app.js wires ever drift apart. import { esc, uiPrompt } from './dom.js'; import { _audioSeek, _audioTime } from './transport.js'; +import { formatTime } from './format.js'; import { host } from './host.js'; import { _setSectionPracticeMode, @@ -167,11 +168,11 @@ export function updateLoopUI() { const label = document.getElementById('loop-label'); const hasLoop = loopA !== null && loopB !== null; if (hasLoop) { - label.textContent = `${host.formatTime(loopA)} → ${host.formatTime(loopB)}`; + label.textContent = `${formatTime(loopA)} → ${formatTime(loopB)}`; document.getElementById('btn-loop-clear').classList.remove('hidden'); document.getElementById('btn-loop-save').classList.remove('hidden'); } else if (loopA !== null) { - label.textContent = `${host.formatTime(loopA)} → ?`; + label.textContent = `${formatTime(loopA)} → ?`; document.getElementById('btn-loop-clear').classList.add('hidden'); document.getElementById('btn-loop-save').classList.add('hidden'); } else { @@ -190,7 +191,7 @@ export async function loadSavedLoops() { sel.innerHTML = ''; for (const l of loops) { - sel.innerHTML += ``; + sel.innerHTML += ``; } if (loops.length > 0) { sel.classList.remove('hidden'); diff --git a/static/js/section-practice.js b/static/js/section-practice.js index 741ca58..338737b 100644 --- a/static/js/section-practice.js +++ b/static/js/section-practice.js @@ -28,6 +28,7 @@ import { audio } from './audio-el.js'; import { esc } from './dom.js'; import { _audioDuration, _audioTime, audioSeekGen } from './transport.js'; +import { formatTime } from './format.js'; import { host } from './host.js'; export function _sectionPracticeBarContains(el) { @@ -902,7 +903,7 @@ export function renderSectionPracticeBar() { _showSectionPracticeBar(bar); scroll.innerHTML = parents.map((p, i) => { const label = _formatSectionPracticeName(p.name); - const tip = `${label} (${host.formatTime(p.start)}–${host.formatTime(p.end)})`; + const tip = `${label} (${formatTime(p.start)}–${formatTime(p.end)})`; const kindClass = _sectionPracticeChipKindClass(p.name, i); return ``; }).join('');