function createTodayBreak({ timer, sheet, openButtons = null, cancelButton, presetButtons = null, form = null, minutesInput = null, error = null, status, resumeButton, qs = null, queryAll = null, historyRef = null, windowRef = null, now = () => Date.now(), setIntervalRef = setInterval, clearIntervalRef = clearInterval, onChange = () => {}, onResume = () => {}, }) { queryAll ||= selector => globalThis.document?.querySelectorAll(selector) || []; historyRef ||= globalThis.history; windowRef ||= globalThis.window; sheet ||= qs?.('#today-break-sheet'); openButtons ||= queryAll?.('[data-today-break-open]') || []; cancelButton ||= qs?.('#cancel-today-break'); presetButtons ||= queryAll?.('[data-today-break-minutes]') || []; form ||= qs?.('#today-break-custom-form'); minutesInput ||= qs?.('#today-break-custom-minutes'); error ||= qs?.('#today-break-error'); status ||= qs?.('#today-break-status'); resumeButton ||= qs?.('#resume-today-break'); let ticker = null; let historyEntry = false; const stopTicker = () => { if (ticker !== null) clearIntervalRef(ticker); ticker = null; }; const render = () => { const pending = timer.breakSnapshot(); if (!pending) { stopTicker(); status.hidden = true; resumeButton.hidden = true; openButtons.forEach(button => { button.hidden = false; }); return false; } status.hidden = false; resumeButton.hidden = false; openButtons.forEach(button => { button.hidden = true; }); const remaining = Math.max(0, pending.deadline_at - now()); if (pending.expired || remaining <= 0) { status.textContent = 'Break over · ready to resume'; } else { const seconds = Math.ceil(remaining / 1000); const minutes = Math.floor(seconds / 60); status.textContent = 'On break · resume in ' + minutes + ':' + String(seconds % 60).padStart(2, '0'); } if (ticker === null) ticker = setIntervalRef(render, 1000); return true; }; const close = () => { if (sheet.open) sheet.close(); if (historyEntry) { historyEntry = false; historyRef?.back(); } }; const start = minutes => { if (!timer.startBreak(Number(minutes))) return false; if (error) error.textContent = ''; close(); render(); onChange(); return true; }; const open = () => { if (!timer.snapshot().identity) return false; if (!sheet.open) { sheet.showModal(); if (historyRef) { historyRef.pushState({...(historyRef.state || {}), todayBreak:true}, ''); historyEntry = true; } } return true; }; openButtons.forEach(button => button.addEventListener('click', open)); cancelButton?.addEventListener('click', close); windowRef?.addEventListener('popstate', () => { historyEntry = false; if (sheet.open) sheet.close(); }); presetButtons.forEach(button => button.addEventListener('click', () => start(button.dataset.todayBreakMinutes) )); form?.addEventListener('submit', event => { event.preventDefault(); if (!start(Number(minutesInput?.value))) { if (error) error.textContent = 'Choose a whole number from 1 to 120 minutes.'; minutesInput?.focus(); } }); resumeButton.addEventListener('click', () => { const pending = timer.breakSnapshot(); if (!timer.resumeBreak()) return; onResume(pending.identity); render(); onChange(); }); render(); return { open, close, start, render }; } if (typeof module !== 'undefined' && module.exports) module.exports = createTodayBreak;