From f45b4c501f31d564a975d3060cc49aaca7de3b4d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Sep 2025 10:58:13 +0000 Subject: [PATCH 01/14] Initial plan From 5e849694787854179c9e3a5fa4cfdba3e56a8efa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Sep 2025 11:08:30 +0000 Subject: [PATCH 02/14] Implement interactive HIIT rounds functionality with timer and round tracking Co-authored-by: Bruno-366 <81762173+Bruno-366@users.noreply.github.com> --- src/lib/stores.ts | 22 ++ src/lib/types.ts | 11 + src/routes/settings/ResetProgress.svelte | 11 + src/routes/workout/+page.svelte | 11 + src/routes/workout/CardioWorkouts.svelte | 267 ++++++++++++++++++++++- 5 files changed, 317 insertions(+), 5 deletions(-) diff --git a/src/lib/stores.ts b/src/lib/stores.ts index eec27f8..beade61 100644 --- a/src/lib/stores.ts +++ b/src/lib/stores.ts @@ -22,6 +22,17 @@ interface UIState { startTime: number pausedTime: number } + hiitTimer: { + isActive: boolean + isPaused: boolean + timeLeft: number + totalTime: number + startTime: number + pausedTime: number + currentRound: number + totalRounds: number + roundCompleted: boolean + } } interface WorkoutState { @@ -199,6 +210,17 @@ const defaultUIState: UIState = { totalTime: 0, startTime: 0, pausedTime: 0 + }, + hiitTimer: { + isActive: false, + isPaused: false, + timeLeft: 0, + totalTime: 0, + startTime: 0, + pausedTime: 0, + currentRound: 0, + totalRounds: 0, + roundCompleted: false } } diff --git a/src/lib/types.ts b/src/lib/types.ts index 6c4ab3f..455da04 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -85,6 +85,17 @@ export interface AppState { startTime: number; pausedTime: number; }; + hiitTimer: { + isActive: boolean; + isPaused: boolean; + timeLeft: number; + totalTime: number; + startTime: number; + pausedTime: number; + currentRound: number; + totalRounds: number; + roundCompleted: boolean; + }; } export interface WarmupSet { diff --git a/src/routes/settings/ResetProgress.svelte b/src/routes/settings/ResetProgress.svelte index 9a3ceb7..ce22441 100644 --- a/src/routes/settings/ResetProgress.svelte +++ b/src/routes/settings/ResetProgress.svelte @@ -43,6 +43,17 @@ totalTime: 0, startTime: 0, pausedTime: 0 + }, + hiitTimer: { + isActive: false, + isPaused: false, + timeLeft: 0, + totalTime: 0, + startTime: 0, + pausedTime: 0, + currentRound: 0, + totalRounds: 0, + roundCompleted: false } }) diff --git a/src/routes/workout/+page.svelte b/src/routes/workout/+page.svelte index 9149e29..5d6da19 100644 --- a/src/routes/workout/+page.svelte +++ b/src/routes/workout/+page.svelte @@ -91,6 +91,17 @@ totalTime: 0, startTime: 0, pausedTime: 0 + }, + hiitTimer: { + isActive: false, + isPaused: false, + timeLeft: 0, + totalTime: 0, + startTime: 0, + pausedTime: 0, + currentRound: 0, + totalRounds: 0, + roundCompleted: false } })) diff --git a/src/routes/workout/CardioWorkouts.svelte b/src/routes/workout/CardioWorkouts.svelte index c383ccd..9ac94b0 100644 --- a/src/routes/workout/CardioWorkouts.svelte +++ b/src/routes/workout/CardioWorkouts.svelte @@ -1,6 +1,6 @@ {#if workout.type === 'liss' || workout.type === 'hiit'} @@ -176,7 +372,13 @@ {cardioWorkout().activity} {#if cardioWorkout().duration !== undefined} -
{displayDuration()}
+
+ {#if workout.type === 'hiit' && showHiitControls()} + {hiitDisplayTime()} + {:else} + {displayDuration()} + {/if} +
{#if workout.type === 'liss'} minutes @@ -197,13 +399,24 @@ {/if} {#if cardioWorkout().rounds !== undefined}
- {cardioWorkout().rounds} rounds + {#if workout.type === 'hiit' && showHiitControls()} + {hiitRoundDisplay()} + {:else} + {cardioWorkout().rounds} rounds + {/if} +
+ {/if} + + {#if workout.type === 'hiit' && hiitWorkoutComplete()} +
+ + All Rounds Complete!
{/if}
{#if showTimerControls()} - +
+ + +
+ + {#if hiitTimer.roundCompleted && !hiitWorkoutComplete()} + +
+ +
+ {/if} + {/if} + - {#if restTimer.workoutType === 'strength' && restTimer.phase !== 'extended' && restTimer.timeLeft === 0} + {#if workoutType === 'strength' && phase !== 'extended' && timeLeft === 0} - - - + {@const workout = cardioWorkout()} + {@const isRoundsOnly = workout.duration === undefined && workout.rounds !== undefined} + {@const buttonStates = hiitButtonStates()} - {#if hiitTimer.roundCompleted && !hiitWorkoutComplete()} - -
+ {#if isRoundsOnly} + +
+ + {#if hiitTimer.currentRound > 0 && !hiitWorkoutComplete() && buttonStates.complete} + + {/if}
+ {:else} + +
+ + {#if buttonStates.pause} + + {/if} + +
+ + {#if hiitTimer.roundCompleted && !hiitWorkoutComplete() && buttonStates.nextRound} + +
+ +
+ {/if} {/if} {/if} diff --git a/src/routes/workout/RestTimer.svelte b/src/routes/workout/RestTimer.svelte index f08611b..83d6eed 100644 --- a/src/routes/workout/RestTimer.svelte +++ b/src/routes/workout/RestTimer.svelte @@ -3,67 +3,72 @@ import { showRestCompleteNotification } from '$lib/utils' interface RestTimerProps { - isActive: boolean - timeLeft: number - totalTime: number - workoutType: 'strength' | 'hypertrophy' | null - phase: 'initial' | 'extended' - startTime: number - onUpdate: (updates: Partial) => void + workoutType: 'strength' | 'hypertrophy' onStop: () => void } - interface RestTimerState { - isActive: boolean - timeLeft: number - totalTime: number - workoutType: 'strength' | 'hypertrophy' | null - phase: 'initial' | 'extended' - startTime: number - } + let { workoutType, onStop }: RestTimerProps = $props() - let { - isActive, - timeLeft, - totalTime, - workoutType, - phase, - startTime, - onUpdate, - onStop - }: RestTimerProps = $props() + // Local rest timer state using runes - managed internally + let restTimer = $state({ + isActive: false, + timeLeft: 0, + totalTime: 0, + phase: 'initial' as 'initial' | 'extended', + startTime: 0 + }) + + // Start the rest timer + export const start = () => { + const initialTime = workoutType === 'strength' ? 180 : 90 // 3 min for strength, 1.5 min for hypertrophy + const now = Date.now() + + restTimer.isActive = true + restTimer.timeLeft = initialTime + restTimer.totalTime = initialTime + restTimer.phase = 'initial' + restTimer.startTime = now + } // Extend rest timer function - matching original React implementation const extendRestTimer = () => { - if (workoutType === 'strength' && phase === 'initial') { + if (workoutType === 'strength' && restTimer.phase === 'initial') { const extendedTime = 120 // Additional 2 minutes to reach 5 minutes total const now = Date.now() - onUpdate({ - timeLeft: extendedTime, - totalTime: extendedTime, - phase: 'extended', - startTime: now - }) + restTimer.timeLeft = extendedTime + restTimer.totalTime = extendedTime + restTimer.phase = 'extended' + restTimer.startTime = now } } + // Stop rest timer function + const stopRestTimer = () => { + restTimer.isActive = false + restTimer.timeLeft = 0 + restTimer.totalTime = 0 + restTimer.phase = 'initial' + restTimer.startTime = 0 + onStop() + } + // Derived progress percentage const progressPercent = $derived(() => { - if (totalTime === 0) return 0 - return ((totalTime - timeLeft) / totalTime) * 100 + if (restTimer.totalTime === 0) return 0 + return ((restTimer.totalTime - restTimer.timeLeft) / restTimer.totalTime) * 100 }) // Timer interval effect - timestamp-based to prevent background throttling $effect(() => { let interval: number | undefined - if (isActive && timeLeft > 0) { + if (restTimer.isActive && restTimer.timeLeft > 0) { interval = setInterval(() => { const now = Date.now() - const elapsedSeconds = Math.floor((now - startTime) / 1000) - const newTimeLeft = Math.max(0, totalTime - elapsedSeconds) + const elapsedSeconds = Math.floor((now - restTimer.startTime) / 1000) + const newTimeLeft = Math.max(0, restTimer.totalTime - elapsedSeconds) - onUpdate({ timeLeft: newTimeLeft }) + restTimer.timeLeft = newTimeLeft }, 100) // Check more frequently for smoother updates } @@ -74,30 +79,30 @@ // Separate effect to handle notification when timer reaches 0 $effect(() => { - if (isActive && timeLeft === 0) { + if (restTimer.isActive && restTimer.timeLeft === 0) { showRestCompleteNotification() } }) -{#if isActive} +{#if restTimer.isActive}
- {phase === 'extended' ? 'Extended Rest' : 'Rest Time'} + {restTimer.phase === 'extended' ? 'Extended Rest' : 'Rest Time'}
- {Math.floor(timeLeft / 60)}:{(timeLeft % 60).toString().padStart(2, '0')} + {Math.floor(restTimer.timeLeft / 60)}:{(restTimer.timeLeft % 60).toString().padStart(2, '0')}
@@ -105,14 +110,14 @@
- {#if workoutType === 'strength' && phase !== 'extended' && timeLeft === 0} + {#if workoutType === 'strength' && restTimer.phase !== 'extended' && restTimer.timeLeft === 0} - {#if hiitTimer.currentRound > 0 && !hiitWorkoutComplete() && buttonStates.complete} + {#if cardioTimer.currentRound > 0 && !hiitWorkoutComplete() && states.complete} {/if} - {#if buttonStates.pause} + {#if states.pause} {/if}
- {#if hiitTimer.roundCompleted && !hiitWorkoutComplete() && buttonStates.nextRound} + {#if cardioTimer.roundCompleted && !hiitWorkoutComplete() && states.nextRound}
{/if} From d1ceacff342d6752c27bd6951b88d6b6b4a7f69d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Sep 2025 21:39:23 +0000 Subject: [PATCH 08/14] Refactor navigation to use URL-based routing and remove redundant UI state Co-authored-by: Bruno-366 <81762173+Bruno-366@users.noreply.github.com> --- src/lib/stores.ts | 11 ----------- src/routes/workout/+page.svelte | 8 +------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/lib/stores.ts b/src/lib/stores.ts index 47af84a..844f960 100644 --- a/src/lib/stores.ts +++ b/src/lib/stores.ts @@ -3,10 +3,6 @@ import { writable } from 'svelte/store' import type { CompletedWorkout, CustomPlanBlock } from '$lib/types' // Store interfaces -interface UIState { - activeTab: string -} - interface WorkoutState { currentWeek: number currentDay: number @@ -163,13 +159,6 @@ const createPersistedStore = ( } } -// UI Store (not persisted - transient state) -const defaultUIState: UIState = { - activeTab: 'overview' -} - -export const uiStore = writable(defaultUIState) - // Workout Store (persisted) export const defaultWorkoutState: WorkoutState = { currentWeek: 1, diff --git a/src/routes/workout/+page.svelte b/src/routes/workout/+page.svelte index 34b378b..08f332b 100644 --- a/src/routes/workout/+page.svelte +++ b/src/routes/workout/+page.svelte @@ -30,7 +30,7 @@ const handleCompleteWorkout = async () => { // Import stores dynamically to ensure they're available - const { workoutStore, trainingPlanStore, uiStore } = await import('$lib/stores') + const { workoutStore, trainingPlanStore } = await import('$lib/stores') const { get } = await import('svelte/store') const workoutState = get(workoutStore) @@ -72,12 +72,6 @@ })) } - // Reset UI state (only activeTab now since timers are component-level) - uiStore.update(state => ({ - ...state, - activeTab: 'overview' - })) - goto('/') } } From e2187c7e63c159c7b86afa80dd5e53906a208f23 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Sep 2025 21:59:48 +0000 Subject: [PATCH 09/14] Split CardioWorkouts into separate HIIT and LISS components Co-authored-by: Bruno-366 <81762173+Bruno-366@users.noreply.github.com> --- src/routes/workout/CardioWorkouts.svelte | 524 +---------------------- src/routes/workout/HIITWorkout.svelte | 286 +++++++++++++ src/routes/workout/LISSWorkout.svelte | 199 +++++++++ 3 files changed, 497 insertions(+), 512 deletions(-) create mode 100644 src/routes/workout/HIITWorkout.svelte create mode 100644 src/routes/workout/LISSWorkout.svelte diff --git a/src/routes/workout/CardioWorkouts.svelte b/src/routes/workout/CardioWorkouts.svelte index 387fb65..546d35c 100644 --- a/src/routes/workout/CardioWorkouts.svelte +++ b/src/routes/workout/CardioWorkouts.svelte @@ -1,7 +1,7 @@ -{#if workout.type === 'liss' || workout.type === 'hiit'} -
-
-

- {cardioWorkout().activity} -

- {#if cardioWorkout().duration !== undefined} -
- {#if workout.type === 'hiit' && showHiitControls()} - {hiitDisplayTime()} - {:else} - {displayDuration()} - {/if} -
-
- {#if workout.type === 'liss'} - minutes - {:else} - seconds - {/if} -
- {/if} - {#if cardioWorkout().distance !== undefined} -
{cardioWorkout().distance}
-
- {#if workout.type === 'liss'} - km - {:else} - m - {/if} -
- {/if} - {#if cardioWorkout().rounds !== undefined} -
- {#if workout.type === 'hiit' && showHiitControls()} - {hiitRoundDisplay()} - {:else} - {cardioWorkout().rounds} rounds - {/if} -
- {/if} - - {#if workout.type === 'hiit' && hiitWorkoutComplete()} -
- - All Rounds Complete! -
- {/if} -
- - {#if showTimerControls()} - -
- - - -
- {/if} - - {#if showHiitControls()} - {@const workout = cardioWorkout()} - {@const isRoundsOnly = workout.duration === undefined && workout.rounds !== undefined} - {@const states = buttonStates()} - - {#if isRoundsOnly} - -
- - {#if cardioTimer.currentRound > 0 && !hiitWorkoutComplete() && states.complete} - - {/if} - -
- {:else} - -
- - {#if states.pause} - - {/if} - -
- - {#if cardioTimer.roundCompleted && !hiitWorkoutComplete() && states.nextRound} - -
- -
- {/if} - {/if} - {/if} - - -
+{#if cardioWorkout().type === 'liss'} + +{:else if cardioWorkout().type === 'hiit'} + {/if} \ No newline at end of file diff --git a/src/routes/workout/HIITWorkout.svelte b/src/routes/workout/HIITWorkout.svelte new file mode 100644 index 0000000..f25ccef --- /dev/null +++ b/src/routes/workout/HIITWorkout.svelte @@ -0,0 +1,286 @@ + + +
+
+

{workoutData().activity}

+ + {#if workoutData().duration !== undefined} +
{displayTime()}
+
seconds
+ {/if} + + {#if workoutData().distance !== undefined} +
{workoutData().distance}
+
m
+ {/if} + + {#if hasRounds()} +
{roundDisplay()}
+ {/if} + + {#if workoutComplete()} +
+ + All Rounds Complete! +
+ {/if} +
+ + {#if hasRounds()} + {@const states = buttonStates()} + + {#if isRoundsOnly()} + +
+ + {#if hiitTimer.currentRound > 0 && !workoutComplete() && states.complete} + + {/if} + +
+ {:else} + +
+ + {#if states.pause} + + {/if} + +
+ + {#if hiitTimer.roundCompleted && !workoutComplete() && states.nextRound} +
+ +
+ {/if} + {/if} + {/if} + + +
\ No newline at end of file diff --git a/src/routes/workout/LISSWorkout.svelte b/src/routes/workout/LISSWorkout.svelte new file mode 100644 index 0000000..5dc3466 --- /dev/null +++ b/src/routes/workout/LISSWorkout.svelte @@ -0,0 +1,199 @@ + + +
+
+

+ {cardioWorkout().activity} +

+ {#if cardioWorkout().duration !== undefined} +
+ {displayDuration()} +
+
minutes
+ {/if} + {#if cardioWorkout().distance !== undefined} +
{cardioWorkout().distance}
+
km
+ {/if} + {#if cardioWorkout().rounds !== undefined} +
+ {cardioWorkout().rounds} rounds +
+ {/if} +
+ + {#if showTimerControls()} + +
+ + + +
+ {/if} + + +
\ No newline at end of file From 873d58b63ae9b729532bad8dca357aa734314b35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Sep 2025 08:40:22 +0000 Subject: [PATCH 10/14] Refactor cardio components: integrate CardioWorkouts into +page.svelte and use scoped CSS styling Co-authored-by: Bruno-366 <81762173+Bruno-366@users.noreply.github.com> --- src/routes/workout/+page.svelte | 19 ++- src/routes/workout/CardioWorkouts.svelte | 27 ----- src/routes/workout/HIITWorkout.svelte | 140 ++++++++++++++++++----- src/routes/workout/LISSWorkout.svelte | 70 +++++++++--- 4 files changed, 182 insertions(+), 74 deletions(-) delete mode 100644 src/routes/workout/CardioWorkouts.svelte diff --git a/src/routes/workout/+page.svelte b/src/routes/workout/+page.svelte index 08f332b..395d283 100644 --- a/src/routes/workout/+page.svelte +++ b/src/routes/workout/+page.svelte @@ -1,6 +1,8 @@ - -{#if cardioWorkout().type === 'liss'} - -{:else if cardioWorkout().type === 'hiit'} - -{/if} \ No newline at end of file diff --git a/src/routes/workout/HIITWorkout.svelte b/src/routes/workout/HIITWorkout.svelte index f25ccef..326b101 100644 --- a/src/routes/workout/HIITWorkout.svelte +++ b/src/routes/workout/HIITWorkout.svelte @@ -143,12 +143,8 @@ : `${workoutData().rounds} rounds` }) - // Button states for conditional styling + // Button states for conditional logic only (no styling) const buttonStates = $derived(() => { - const baseClass = 'font-semibold py-3 rounded-lg transition-all flex items-center justify-center gap-2' - const enabled = 'opacity-100' - const disabled = 'opacity-40 cursor-not-allowed' - if (isRoundsOnly()) { const startDisabled = workoutComplete() const completeDisabled = hiitTimer.currentRound === 0 || workoutComplete() @@ -157,16 +153,13 @@ start: { disabled: startDisabled, text: hiitTimer.currentRound === 0 ? 'Start Round 1' : - workoutComplete() ? 'Workout Complete' : `Start Round ${hiitTimer.currentRound}`, - class: `flex-1 bg-green-500 text-white ${baseClass} ${startDisabled ? disabled : `hover:bg-green-600 ${enabled}`}` + workoutComplete() ? 'Workout Complete' : `Start Round ${hiitTimer.currentRound}` }, complete: { - disabled: completeDisabled, - class: `flex-1 bg-blue-500 text-white ${baseClass} ${completeDisabled ? disabled : `hover:bg-blue-600 ${enabled}`}` + disabled: completeDisabled }, stop: { - disabled: hiitTimer.currentRound === 0, - class: `flex-1 bg-red-500 text-white ${baseClass} ${hiitTimer.currentRound === 0 ? disabled : `hover:bg-red-600 ${enabled}`}` + disabled: hiitTimer.currentRound === 0 } } } @@ -181,20 +174,16 @@ start: { disabled: startDisabled, text: hiitTimer.currentRound === 0 ? 'Start Round 1' : - hiitTimer.roundCompleted ? `Start Round ${hiitTimer.currentRound}` : 'Resume Round', - class: `flex-1 bg-green-500 text-white ${baseClass} ${startDisabled ? disabled : `hover:bg-green-600 ${enabled}`}` + hiitTimer.roundCompleted ? `Start Round ${hiitTimer.currentRound}` : 'Resume Round' }, pause: { - disabled: pauseDisabled, - class: `flex-1 bg-yellow-500 text-white ${baseClass} ${pauseDisabled ? disabled : `hover:bg-yellow-600 ${enabled}`}` + disabled: pauseDisabled }, stop: { - disabled: stopDisabled, - class: `flex-1 bg-red-500 text-white ${baseClass} ${stopDisabled ? disabled : `hover:bg-red-600 ${enabled}`}` + disabled: stopDisabled }, nextRound: { - disabled: nextDisabled, - class: `w-full bg-blue-500 text-white ${baseClass} ${nextDisabled ? disabled : `hover:bg-blue-600 ${enabled}`}` + disabled: nextDisabled } } }) @@ -232,17 +221,29 @@ {#if isRoundsOnly()}
- {#if hiitTimer.currentRound > 0 && !workoutComplete() && states.complete} - {/if} - @@ -250,17 +251,29 @@ {:else}
- {#if states.pause} - {/if} - @@ -268,7 +281,11 @@ {#if hiitTimer.roundCompleted && !workoutComplete() && states.nextRound}
- @@ -283,4 +300,73 @@ > Complete HIIT Cardio -
\ No newline at end of file +
+ + \ No newline at end of file diff --git a/src/routes/workout/LISSWorkout.svelte b/src/routes/workout/LISSWorkout.svelte index 5dc3466..a441db3 100644 --- a/src/routes/workout/LISSWorkout.svelte +++ b/src/routes/workout/LISSWorkout.svelte @@ -111,28 +111,21 @@ return cardioWorkout().duration !== undefined }) - // Button states for conditional styling + // Button states for conditional logic only (no styling) const buttonStates = $derived(() => { - const baseButtonClass = 'font-semibold py-3 rounded-lg transition-all flex items-center justify-center gap-2' - const enabledClass = 'opacity-100' - const disabledClass = 'opacity-40 cursor-not-allowed' - const isStartDisabled = lissTimer.isActive const isPauseDisabled = !lissTimer.isActive const isStopDisabled = !lissTimer.isActive && !lissTimer.isPaused return { start: { - disabled: isStartDisabled, - class: `flex-1 bg-green-500 text-white ${baseButtonClass} ${isStartDisabled ? disabledClass : `hover:bg-green-600 ${enabledClass}`}` + disabled: isStartDisabled }, pause: { - disabled: isPauseDisabled, - class: `flex-1 bg-yellow-500 text-white ${baseButtonClass} ${isPauseDisabled ? disabledClass : `hover:bg-yellow-600 ${enabledClass}`}` + disabled: isPauseDisabled }, stop: { - disabled: isStopDisabled, - class: `flex-1 bg-red-500 text-white ${baseButtonClass} ${isStopDisabled ? disabledClass : `hover:bg-red-600 ${enabledClass}`}` + disabled: isStopDisabled } } }) @@ -166,7 +159,7 @@ -
\ No newline at end of file +
+ + \ No newline at end of file From 4c6c2d9b2e58fa04f62797fc2463e29836a8eab7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Sep 2025 09:26:51 +0000 Subject: [PATCH 11/14] Improve HIIT workout UX - unified logic, remove duplicate buttons, fix rounds-only workouts Co-authored-by: Bruno-366 <81762173+Bruno-366@users.noreply.github.com> --- src/routes/workout/HIITWorkout.svelte | 203 +++++++++----------------- src/routes/workout/LISSWorkout.svelte | 13 +- 2 files changed, 75 insertions(+), 141 deletions(-) diff --git a/src/routes/workout/HIITWorkout.svelte b/src/routes/workout/HIITWorkout.svelte index 326b101..d415894 100644 --- a/src/routes/workout/HIITWorkout.svelte +++ b/src/routes/workout/HIITWorkout.svelte @@ -31,21 +31,9 @@ const { duration, rounds } = workoutData() if (!rounds) return - // Rounds-only workouts (Hill Sprints) - if (!duration) { - if (hiitTimer.currentRound === 0) { - Object.assign(hiitTimer, { - isActive: false, isPaused: false, timeLeft: 0, totalTime: 0, - startTime: 0, pausedTime: 0, currentRound: 1, totalRounds: rounds, roundCompleted: true - }) - } else if (hiitTimer.currentRound < hiitTimer.totalRounds) { - hiitTimer.currentRound++ - hiitTimer.roundCompleted = true - } - return - } + // Use -1 duration for rounds-only workouts (Hill Sprints) + const effectiveDuration = duration ?? -1 - // Timed workouts const now = Date.now() if (hiitTimer.isPaused) { hiitTimer.isActive = true @@ -53,13 +41,19 @@ hiitTimer.startTime = now - (hiitTimer.totalTime - hiitTimer.timeLeft) * 1000 } else if (hiitTimer.currentRound === 0) { Object.assign(hiitTimer, { - isActive: true, isPaused: false, timeLeft: duration, totalTime: duration, - startTime: now, pausedTime: 0, currentRound: 1, totalRounds: rounds, roundCompleted: false + isActive: effectiveDuration > 0, isPaused: false, + timeLeft: effectiveDuration > 0 ? effectiveDuration : 0, + totalTime: effectiveDuration > 0 ? effectiveDuration : 0, + startTime: now, pausedTime: 0, currentRound: 1, totalRounds: rounds, + roundCompleted: effectiveDuration === -1 }) } else { Object.assign(hiitTimer, { - isActive: true, isPaused: false, timeLeft: duration, totalTime: duration, - startTime: now, pausedTime: 0, roundCompleted: false + isActive: effectiveDuration > 0, isPaused: false, + timeLeft: effectiveDuration > 0 ? effectiveDuration : 0, + totalTime: effectiveDuration > 0 ? effectiveDuration : 0, + startTime: now, pausedTime: 0, + roundCompleted: effectiveDuration === -1 }) } } @@ -79,10 +73,16 @@ const completeCurrentRound = () => { if (hiitTimer.currentRound < hiitTimer.totalRounds) { - Object.assign(hiitTimer, { - isActive: false, isPaused: false, currentRound: hiitTimer.currentRound + 1, - roundCompleted: true, timeLeft: 0 - }) + // For rounds-only workouts, directly advance to next round + if (workoutData().duration === undefined) { + hiitTimer.currentRound++ + hiitTimer.roundCompleted = true + } else { + Object.assign(hiitTimer, { + isActive: false, isPaused: false, currentRound: hiitTimer.currentRound + 1, + roundCompleted: true, timeLeft: 0 + }) + } } else { Object.assign(hiitTimer, { isActive: false, isPaused: false, roundCompleted: true, timeLeft: 0 @@ -90,10 +90,6 @@ } } - const markRoundComplete = () => { - if (isRoundsOnly()) completeCurrentRound() - } - // Timer interval effect - timestamp-based to prevent background throttling $effect(() => { let interval: number | undefined @@ -124,10 +120,12 @@ // Derived display values const workoutData = $derived(() => cardioWorkout()) const hasRounds = $derived(() => workoutData().rounds !== undefined) - const isRoundsOnly = $derived(() => workoutData().duration === undefined && hasRounds()) const workoutComplete = $derived(() => hiitTimer.totalRounds > 0 && hiitTimer.currentRound > hiitTimer.totalRounds) const displayTime = $derived(() => { + // Don't show time for rounds-only workouts + if (workoutData().duration === undefined) return undefined + if (hiitTimer.isActive || hiitTimer.isPaused || hiitTimer.timeLeft > 0) { const minutes = Math.floor(hiitTimer.timeLeft / 60) const seconds = hiitTimer.timeLeft % 60 @@ -145,28 +143,8 @@ // Button states for conditional logic only (no styling) const buttonStates = $derived(() => { - if (isRoundsOnly()) { - const startDisabled = workoutComplete() - const completeDisabled = hiitTimer.currentRound === 0 || workoutComplete() - - return { - start: { - disabled: startDisabled, - text: hiitTimer.currentRound === 0 ? 'Start Round 1' : - workoutComplete() ? 'Workout Complete' : `Start Round ${hiitTimer.currentRound}` - }, - complete: { - disabled: completeDisabled - }, - stop: { - disabled: hiitTimer.currentRound === 0 - } - } - } - - // Timed workouts const startDisabled = hiitTimer.isActive - const pauseDisabled = !hiitTimer.isActive + const pauseDisabled = !hiitTimer.isActive || workoutData().duration === undefined const stopDisabled = !hiitTimer.isActive && !hiitTimer.isPaused && hiitTimer.currentRound === 0 const nextDisabled = hiitTimer.isActive || hiitTimer.currentRound === 0 || workoutComplete() @@ -193,7 +171,7 @@

{workoutData().activity}

- {#if workoutData().duration !== undefined} + {#if displayTime() !== undefined}
{displayTime()}
seconds
{/if} @@ -218,79 +196,47 @@ {#if hasRounds()} {@const states = buttonStates()} - {#if isRoundsOnly()} - -
- - {#if hiitTimer.currentRound > 0 && !workoutComplete() && states.complete} - - {/if} - -
- {:else} - -
+ +
+ + {#if workoutData().duration !== undefined && states.pause} - {#if states.pause} - - {/if} + {/if} + +
+ + {#if hiitTimer.roundCompleted && !workoutComplete() && states.nextRound && workoutData().duration !== undefined} +
- - {#if hiitTimer.roundCompleted && !workoutComplete() && states.nextRound} -
- -
- {/if} {/if} {/if} @@ -306,7 +252,6 @@ .btn-start, .btn-pause, .btn-stop, - .btn-complete, .btn-next-round { font-weight: 600; padding: 0.75rem; @@ -317,52 +262,42 @@ justify-content: center; gap: 0.5rem; color: white; - opacity: 1; } .btn-start { flex: 1; - background-color: rgb(34 197 94); + background-color: #22c55e; /* green-500 */ } .btn-start:not(.disabled):hover { - background-color: rgb(22 163 74); + background-color: #16a34a; /* green-600 */ } .btn-pause { flex: 1; - background-color: rgb(234 179 8); + background-color: #eab308; /* yellow-500 */ } .btn-pause:not(.disabled):hover { - background-color: rgb(202 138 4); + background-color: #ca8a04; /* yellow-600 */ } .btn-stop { flex: 1; - background-color: rgb(239 68 68); + background-color: #ef4444; /* red-500 */ } .btn-stop:not(.disabled):hover { - background-color: rgb(220 38 38); - } - - .btn-complete { - flex: 1; - background-color: rgb(59 130 246); - } - - .btn-complete:not(.disabled):hover { - background-color: rgb(37 99 235); + background-color: #dc2626; /* red-600 */ } .btn-next-round { width: 100%; - background-color: rgb(59 130 246); + background-color: #3b82f6; /* blue-500 */ } .btn-next-round:not(.disabled):hover { - background-color: rgb(37 99 235); + background-color: #2563eb; /* blue-600 */ } .disabled { diff --git a/src/routes/workout/LISSWorkout.svelte b/src/routes/workout/LISSWorkout.svelte index a441db3..0655136 100644 --- a/src/routes/workout/LISSWorkout.svelte +++ b/src/routes/workout/LISSWorkout.svelte @@ -205,31 +205,30 @@ justify-content: center; gap: 0.5rem; color: white; - opacity: 1; } .btn-start { - background-color: rgb(34 197 94); + background-color: #22c55e; /* green-500 */ } .btn-start:not(.disabled):hover { - background-color: rgb(22 163 74); + background-color: #16a34a; /* green-600 */ } .btn-pause { - background-color: rgb(234 179 8); + background-color: #eab308; /* yellow-500 */ } .btn-pause:not(.disabled):hover { - background-color: rgb(202 138 4); + background-color: #ca8a04; /* yellow-600 */ } .btn-stop { - background-color: rgb(239 68 68); + background-color: #ef4444; /* red-500 */ } .btn-stop:not(.disabled):hover { - background-color: rgb(220 38 38); + background-color: #dc2626; /* red-600 */ } .disabled { From 978bdbba5de438b326cf96754f015ed283585b30 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Sep 2025 10:05:01 +0000 Subject: [PATCH 12/14] Fix HIIT workout issues: remove duplicate blue button, fix rounds-only progression, implement Tailwind @apply directives Co-authored-by: Bruno-366 <81762173+Bruno-366@users.noreply.github.com> --- src/routes/workout/HIITWorkout.svelte | 127 ++++++++++---------------- src/routes/workout/LISSWorkout.svelte | 38 +++----- 2 files changed, 61 insertions(+), 104 deletions(-) diff --git a/src/routes/workout/HIITWorkout.svelte b/src/routes/workout/HIITWorkout.svelte index d415894..f67a936 100644 --- a/src/routes/workout/HIITWorkout.svelte +++ b/src/routes/workout/HIITWorkout.svelte @@ -1,6 +1,6 @@