@@ -45,7 +45,11 @@ export default function ActiveWorkoutView({ workout, exercises, sessionSets, onF
4545 const trimmed = draftName . trim ( )
4646 if ( trimmed && trimmed !== workout . name ) onRename ?. ( workout . id , trimmed )
4747 }
48- const [ selectedExId , setSelectedExId ] = React . useState ( null )
48+ // Auto-select the last-worked exercise so the log row is ready on re-open
49+ const [ selectedExId , setSelectedExId ] = React . useState ( ( ) => {
50+ if ( sessionSets . length > 0 ) return sessionSets [ sessionSets . length - 1 ] . exercise_id
51+ return null
52+ } )
4953 const [ showExPicker , setShowExPicker ] = React . useState ( false )
5054 const [ exSearch , setExSearch ] = React . useState ( '' )
5155 const [ reps , setReps ] = React . useState ( '' )
@@ -58,6 +62,7 @@ export default function ActiveWorkoutView({ workout, exercises, sessionSets, onF
5862 const [ restRunning , setRestRunning ] = React . useState ( false )
5963 const restEndRef = React . useRef ( null )
6064 const swipeTouchRef = React . useRef ( null )
65+ const exSearchInputRef = React . useRef ( null )
6166 const [ logPulseActive , setLogPulseActive ] = React . useState ( false )
6267 const [ prFlashExId , setPrFlashExId ] = React . useState ( null )
6368
@@ -239,6 +244,14 @@ export default function ActiveWorkoutView({ workout, exercises, sessionSets, onF
239244 }
240245 } , [ _activeSessionCount ] )
241246
247+ // Focus search input when exercise picker opens (autoFocus is unreliable on iOS Safari)
248+ React . useEffect ( ( ) => {
249+ if ( showExPicker ) {
250+ const t = setTimeout ( ( ) => exSearchInputRef . current ?. focus ( ) , 80 )
251+ return ( ) => clearTimeout ( t )
252+ }
253+ } , [ showExPicker ] )
254+
242255 async function handleLogSet ( ) {
243256 if ( ! selectedExId || ( ! reps && ! weight ) ) return
244257 const weightKg = weight ? parseWeight ( weight , unit ) : null
@@ -282,7 +295,7 @@ export default function ActiveWorkoutView({ workout, exercises, sessionSets, onF
282295
283296 return (
284297 < div className = { [ liquidGlass ? 'liquid-glass' : '' , animationsEnabled ? '' : 'no-anim' ] . filter ( Boolean ) . join ( ' ' ) || undefined }
285- style = { { minHeight : '100vh' , display : 'flex' , flexDirection : 'column ', background : 'var(--bg)' } }
298+ style = { { minHeight : '100dvh ' , background : 'var(--bg)' } }
286299 onTouchStart = { e => { swipeTouchRef . current = { x : e . touches [ 0 ] . clientX , y : e . touches [ 0 ] . clientY } } }
287300 onTouchEnd = { e => {
288301 if ( ! swipeTouchRef . current ) return
@@ -291,7 +304,8 @@ export default function ActiveWorkoutView({ workout, exercises, sessionSets, onF
291304 swipeTouchRef . current = null
292305 if ( dx > 80 && Math . abs ( dx ) > Math . abs ( dy ) * 1.5 ) onExit ?. ( )
293306 } } >
294- < div style = { { position : 'sticky' , top : 0 , zIndex : 10 , background : 'var(--bg)' , borderBottom : '1px solid var(--border)' , padding : 'calc(14px + env(safe-area-inset-top)) 16px 12px' , display : 'flex' , alignItems : 'flex-start' , gap : 12 } } >
307+ < div style = { { position : 'sticky' , top : 0 , zIndex : 10 } } >
308+ < div style = { { background : 'var(--bg)' , borderBottom : '1px solid var(--border)' , padding : 'calc(14px + env(safe-area-inset-top)) 16px 12px' , display : 'flex' , alignItems : 'flex-start' , gap : 12 } } >
295309 < div style = { { flex : 1 , minWidth : 0 } } >
296310 { editingName ? (
297311 < input
@@ -323,22 +337,7 @@ export default function ActiveWorkoutView({ workout, exercises, sessionSets, onF
323337 style = { { background : sessionSets . length === 0 ? 'color-mix(in srgb, var(--danger) 12%, transparent)' : 'var(--bg-secondary)' , border : `1px solid ${ sessionSets . length === 0 ? 'color-mix(in srgb, var(--danger) 35%, transparent)' : 'var(--border)' } ` , color : sessionSets . length === 0 ? 'var(--danger)' : 'var(--text)' , borderRadius : 8 , padding : 0 , width : 36 , height : 36 , cursor : 'pointer' , display : 'flex' , alignItems : 'center' , justifyContent : 'center' , flexShrink : 0 } } > < IconX size = { 16 } /> </ button >
324338 </ div >
325339
326- { /* Cancel confirmation modal */ }
327- { cancelConfirm && (
328- < div className = "glass-overlay" style = { { position : 'fixed' , inset : 0 , zIndex : 200 , display : 'flex' , alignItems : 'center' , justifyContent : 'center' , padding : 24 } } >
329- < div className = "card glass-panel" style = { { width : '100%' , maxWidth : 360 } } >
330- < p className = "section-heading" style = { { marginTop : 0 } } > Cancel workout?</ p >
331- < p className = "muted" style = { { marginBottom : 20 } } > This will delete the workout and all logged sets. This cannot be undone.</ p >
332- < div className = "row" >
333- < button onClick = { ( ) => setCancelConfirm ( false ) } style = { { flex : 1 } } > Keep going</ button >
334- < button onClick = { ( ) => { setCancelConfirm ( false ) ; onCancel ( workout . id ) } }
335- style = { { flex : 1 , background : 'var(--danger)' , color : '#fff' , border : 'none' , borderRadius : 8 , padding : '10px 0' , cursor : 'pointer' , fontWeight : 600 } } > Yes, cancel</ button >
336- </ div >
337- </ div >
338- </ div >
339- ) }
340-
341- { /* Rest timer */ }
340+ { /* Rest timer — sticky with header */ }
342341 { restLeft !== null && (
343342 < div style = { { background : 'var(--accent)' , color : '#fff' , padding : '18px 16px 14px' , textAlign : 'center' } } >
344343 < div style = { { fontSize : '0.75rem' , fontWeight : 700 , letterSpacing : '0.12em' , opacity : 0.85 , marginBottom : 4 , textTransform : 'uppercase' } } > Rest</ div >
@@ -374,9 +373,25 @@ export default function ActiveWorkoutView({ workout, exercises, sessionSets, onF
374373 </ div >
375374 </ div >
376375 ) }
376+ </ div > { /* end sticky wrapper */ }
377377
378- { /* Main scroll area */ }
379- < div style = { { flex : 1 , overflowY : 'auto' , padding : '16px 16px 32px' , display : 'flex' , flexDirection : 'column' , gap : 12 } } >
378+ { /* Cancel confirmation modal */ }
379+ { cancelConfirm && (
380+ < div className = "glass-overlay" style = { { position : 'fixed' , inset : 0 , zIndex : 200 , display : 'flex' , alignItems : 'center' , justifyContent : 'center' , padding : 24 } } >
381+ < div className = "card glass-panel" style = { { width : '100%' , maxWidth : 360 } } >
382+ < p className = "section-heading" style = { { marginTop : 0 } } > Cancel workout?</ p >
383+ < p className = "muted" style = { { marginBottom : 20 } } > This will delete the workout and all logged sets. This cannot be undone.</ p >
384+ < div className = "row" >
385+ < button onClick = { ( ) => setCancelConfirm ( false ) } style = { { flex : 1 } } > Keep going</ button >
386+ < button onClick = { ( ) => { setCancelConfirm ( false ) ; onCancel ( workout . id ) } }
387+ style = { { flex : 1 , background : 'var(--danger)' , color : '#fff' , border : 'none' , borderRadius : 8 , padding : '10px 0' , cursor : 'pointer' , fontWeight : 600 } } > Yes, cancel</ button >
388+ </ div >
389+ </ div >
390+ </ div >
391+ ) }
392+
393+ { /* Main content */ }
394+ < div style = { { padding : '16px 16px 32px' , display : 'flex' , flexDirection : 'column' , gap : 12 } } >
380395
381396 { workout . status === 'in_progress' && (
382397 < >
@@ -604,7 +619,8 @@ export default function ActiveWorkoutView({ workout, exercises, sessionSets, onF
604619 < div style = { { fontWeight : 700 , fontSize : '1.1rem' , marginBottom : 4 } } > Workout complete</ div >
605620 < div style = { { color : 'var(--text-muted)' , fontSize : '0.9rem' } } > { setCount } set{ setCount !== 1 ? 's' : '' } · { workoutTimer } </ div >
606621 </ div >
607- { Object . entries ( setsByExercise ) . map ( ( [ exId , sets ] ) => {
622+ { orderedExIds . map ( exId => {
623+ const sets = setsByExercise [ exId ] || [ ]
608624 const ex = exercises . find ( e => e . id == exId )
609625 return (
610626 < div key = { exId } style = { { marginBottom : 16 } } >
@@ -702,7 +718,7 @@ export default function ActiveWorkoutView({ workout, exercises, sessionSets, onF
702718 < div style = { { position : 'fixed' , inset : 0 , zIndex : 100 , display : 'flex' , flexDirection : 'column' , justifyContent : 'flex-end' } }
703719 onClick = { ( ) => { setShowExPicker ( false ) ; setExSearch ( '' ) } } >
704720 < div className = "glass-overlay" style = { { position : 'absolute' , inset : 0 } } />
705- < div className = "glass-panel" style = { { position : 'relative' , borderRadius : '20px 20px 0 0' , maxHeight : '80vh ' , display : 'flex' , flexDirection : 'column' } }
721+ < div className = "glass-panel" style = { { position : 'relative' , borderRadius : '20px 20px 0 0' , maxHeight : '85dvh ' , display : 'flex' , flexDirection : 'column' } }
706722 onClick = { e => e . stopPropagation ( ) } >
707723 < div style = { { textAlign : 'center' , padding : '10px 0 0' } } >
708724 < div style = { { width : 36 , height : 4 , borderRadius : 2 , background : 'var(--border)' , display : 'inline-block' } } />
@@ -712,11 +728,7 @@ export default function ActiveWorkoutView({ workout, exercises, sessionSets, onF
712728 < button onClick = { ( ) => { setShowExPicker ( false ) ; setExSearch ( '' ) } }
713729 style = { { background : 'none' , border : 'none' , cursor : 'pointer' , color : 'var(--muted)' , padding : 4 , display : 'flex' , alignItems : 'center' } } > < IconX size = { 18 } /> </ button >
714730 </ div >
715- < div style = { { padding : '0 16px 8px' } } >
716- < input autoFocus placeholder = "Search exercises…" value = { exSearch }
717- onChange = { e => setExSearch ( e . target . value ) } style = { { margin : 0 } } />
718- </ div >
719- < div style = { { flex : 1 , overflowY : 'auto' , padding : '0 16px 24px' } } >
731+ < div style = { { flex : 1 , overflowY : 'auto' , padding : '0 16px 8px' } } >
720732 { groupKeys . length === 0 && < div className = "muted small" style = { { padding : '16px 0' } } > No exercises found</ div > }
721733 { groupKeys . map ( group => (
722734 < div key = { group } >
@@ -733,6 +745,11 @@ export default function ActiveWorkoutView({ workout, exercises, sessionSets, onF
733745 </ div >
734746 ) ) }
735747 </ div >
748+ { /* Search input at bottom so it sits just above the keyboard */ }
749+ < div style = { { padding : '8px 16px calc(8px + env(safe-area-inset-bottom))' , borderTop : '1px solid var(--border)' } } >
750+ < input ref = { exSearchInputRef } placeholder = "Search exercises…" value = { exSearch }
751+ onChange = { e => setExSearch ( e . target . value ) } style = { { margin : 0 } } />
752+ </ div >
736753 </ div >
737754 </ div >
738755 ) }
0 commit comments