Skip to content
Merged
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
174 changes: 105 additions & 69 deletions js/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -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()];

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -2030,16 +2059,21 @@ export const printWorkout = () => {
</section>`;
}).join('');

const printHtml = `<!doctype html><html><head><meta charset="utf-8"><title>Workout</title>
<style>
body { font-family: Arial, sans-serif; padding: 24px; color: #111; }
h1 { margin-bottom: 8px; }
section { margin-bottom: 24px; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #ccc; padding: 6px 8px; text-align: left; }
th { background: #f4f4f4; }
</style>
</head><body>
const printHtml = `<!doctype html><html><head><meta charset="utf-8"><title>Workout</title>
<style>
@page {
size: landscape;
margin: 0.5in;
}
body { font-family: Arial, sans-serif; padding: 24px; color: #111; }
h1 { margin-bottom: 8px; }
section { margin-bottom: 24px; page-break-inside: avoid; }
section table { margin-top: 8px; }
table { width: 100%; border-collapse: collapse; table-layout: fixed; }
th, td { border: 1px solid #ccc; padding: 6px 8px; text-align: left; vertical-align: top; white-space: normal; word-break: break-word; overflow-wrap: anywhere; }
th { background: #f4f4f4; }
</style>
</head><body>
<h1>Workout Plan</h1>
<p>Generated ${new Date().toLocaleString()}</p>
${sections}
Expand Down Expand Up @@ -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();
});

Expand Down