From 7c25520bebf7ad1da25509d642c0c709e93c6f94 Mon Sep 17 00:00:00 2001 From: exerciselibrary Date: Sat, 15 Nov 2025 15:57:16 -0500 Subject: [PATCH] Improve Echo builder UX and printing (#77) --- js/builder.js | 174 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 105 insertions(+), 69 deletions(-) diff --git a/js/builder.js b/js/builder.js index a9dfc70..786499a 100644 --- a/js/builder.js +++ b/js/builder.js @@ -791,6 +791,9 @@ const createEntryFromPlanItem = (item, index) => { typeof setData.stopAtTop === 'boolean' ? setData.stopAtTop : Boolean(item?.stopAtTop); + if (set.mode === 'ECHO') { + set.stopAtTop = false; + } applyStoredProgressionConfig(set, setData, item); return set; }; @@ -974,14 +977,17 @@ export const loadPlanIntoBuilder = (planItems = [], options = {}) => { set.justLift = typeof setData.justLift === 'boolean' ? setData.justLift - : Boolean(item?.justLift); - set.stopAtTop = - typeof setData.stopAtTop === 'boolean' - ? setData.stopAtTop - : Boolean(item?.stopAtTop); - - return set; - }); + : Boolean(item?.justLift); + set.stopAtTop = + typeof setData.stopAtTop === 'boolean' + ? setData.stopAtTop + : Boolean(item?.stopAtTop); + if (set.mode === 'ECHO') { + set.stopAtTop = false; + } + + return set; + }); const sets = sortedSets.length ? sortedSets : [createSet()]; @@ -1146,10 +1152,13 @@ export const renderSetRow = (exerciseId, set, index) => { if (typeof set.justLift !== 'boolean') { set.justLift = set.justLift === true || set.justLift === 'true' || set.justLift === 1 || set.justLift === '1'; } - if (typeof set.stopAtTop !== 'boolean') { - set.stopAtTop = - set.stopAtTop === true || set.stopAtTop === 'true' || set.stopAtTop === 1 || set.stopAtTop === '1'; - } + if (typeof set.stopAtTop !== 'boolean') { + set.stopAtTop = + set.stopAtTop === true || set.stopAtTop === 'true' || set.stopAtTop === 1 || set.stopAtTop === '1'; + } + if (set.mode === 'ECHO') { + set.stopAtTop = false; + } const setCell = document.createElement('td'); setCell.textContent = index + 1; @@ -1445,20 +1454,24 @@ export const renderSetRow = (exerciseId, set, index) => { justLiftNote.textContent = 'Always on in Echo Mode'; justLiftCell.append(justLiftWrapper, justLiftNote); - const stopAtTopCell = document.createElement('td'); - stopAtTopCell.className = 'set-flag'; - const stopAtTopWrapper = document.createElement('div'); - stopAtTopWrapper.className = 'flag-control'; - const stopAtTopCheckbox = document.createElement('input'); - stopAtTopCheckbox.type = 'checkbox'; - stopAtTopCheckbox.checked = Boolean(set.stopAtTop); - stopAtTopCheckbox.setAttribute('aria-label', 'Stop at the top of your final rep'); - stopAtTopCheckbox.addEventListener('change', () => { - set.stopAtTop = stopAtTopCheckbox.checked; - persistState(); - }); - stopAtTopWrapper.appendChild(stopAtTopCheckbox); - stopAtTopCell.appendChild(stopAtTopWrapper); + const stopAtTopCell = document.createElement('td'); + stopAtTopCell.className = 'set-flag'; + const stopAtTopWrapper = document.createElement('div'); + stopAtTopWrapper.className = 'flag-control'; + const stopAtTopCheckbox = document.createElement('input'); + stopAtTopCheckbox.type = 'checkbox'; + stopAtTopCheckbox.checked = Boolean(set.stopAtTop); + stopAtTopCheckbox.setAttribute('aria-label', 'Stop at the top of your final rep'); + stopAtTopCheckbox.addEventListener('change', () => { + set.stopAtTop = stopAtTopCheckbox.checked; + persistState(); + }); + stopAtTopWrapper.appendChild(stopAtTopCheckbox); + const stopAtTopNote = document.createElement('div'); + stopAtTopNote.className = 'flag-note muted small'; + stopAtTopNote.textContent = 'Disabled in Echo Mode'; + stopAtTopNote.style.display = 'none'; + stopAtTopCell.append(stopAtTopWrapper, stopAtTopNote); const updateWeightVisibility = () => { const isEcho = set.mode === 'ECHO'; @@ -1491,25 +1504,41 @@ export const renderSetRow = (exerciseId, set, index) => { eccentricWrapper.style.display = isEcho ? '' : 'none'; }; - const updateJustLiftControl = () => { - const isEcho = set.mode === 'ECHO'; - justLiftWrapper.style.display = isEcho ? 'none' : ''; - justLiftCheckbox.disabled = isEcho; - justLiftNote.style.display = isEcho ? '' : 'none'; - }; - - modeSelect.addEventListener('change', () => { - set.mode = modeSelect.value; - if (set.mode === 'ECHO' && !Number.isFinite(Number.parseInt(set.eccentricPct, 10))) { - set.eccentricPct = 100; - } - persistState(); - triggerRender(); - }); - - updateWeightVisibility(); - updateRepEditor(); - updateJustLiftControl(); + const updateJustLiftControl = () => { + const isEcho = set.mode === 'ECHO'; + justLiftWrapper.style.display = isEcho ? 'none' : ''; + justLiftCheckbox.disabled = isEcho; + justLiftNote.style.display = isEcho ? '' : 'none'; + }; + + const updateStopAtTopControl = () => { + const isEcho = set.mode === 'ECHO'; + stopAtTopWrapper.style.display = isEcho ? 'none' : ''; + stopAtTopCheckbox.disabled = isEcho; + stopAtTopNote.style.display = isEcho ? '' : 'none'; + if (isEcho && set.stopAtTop) { + set.stopAtTop = false; + stopAtTopCheckbox.checked = false; + } + }; + + modeSelect.addEventListener('change', () => { + set.mode = modeSelect.value; + if (set.mode === 'ECHO' && !Number.isFinite(Number.parseInt(set.eccentricPct, 10))) { + set.eccentricPct = 100; + } + if (set.mode === 'ECHO' && set.stopAtTop) { + set.stopAtTop = false; + stopAtTopCheckbox.checked = false; + } + persistState(); + triggerRender(); + }); + + updateWeightVisibility(); + updateRepEditor(); + updateJustLiftControl(); + updateStopAtTopControl(); const actionsCell = document.createElement('td'); actionsCell.className = 'set-actions'; @@ -2030,16 +2059,21 @@ export const printWorkout = () => { `; }).join(''); - const printHtml = `Workout - - + const printHtml = `Workout + +

Workout Plan

Generated ${new Date().toLocaleString()}

${sections} @@ -2693,20 +2727,22 @@ const buildBuilderCard = (entry, displayIndex, options = {}) => { quickSelect.value = ''; }; - quickSelect.addEventListener('change', () => { - const chosen = quickSelect.value; - if (!chosen) return; - entry.sets.forEach((set) => { - if (activeQuickMode === PROGRESSION_MODES.PERCENT) { - set.progressionPercent = chosen; - set.progressionMode = PROGRESSION_MODES.PERCENT; - } else { - set.progression = chosen; - set.progressionMode = PROGRESSION_MODES.FLAT; - } - }); - quickSelect.value = ''; - persistState(); + quickSelect.addEventListener('change', () => { + const chosen = quickSelect.value; + if (!chosen) return; + entry.sets.forEach((set) => { + if (activeQuickMode === PROGRESSION_MODES.PERCENT) { + set.progressionPercent = chosen; + set.overloadValue = ''; + set.progressionMode = PROGRESSION_MODES.PERCENT; + } else { + set.overloadValue = chosen; + set.progressionPercent = ''; + set.progressionMode = PROGRESSION_MODES.FLAT; + } + }); + quickSelect.value = ''; + persistState(); triggerRender(); });