diff --git a/Packages/StrandDesign/Sources/StrandDesign/SportIcon.swift b/Packages/StrandDesign/Sources/StrandDesign/SportIcon.swift index 2ecb3548a1..87e1d9bea9 100644 --- a/Packages/StrandDesign/Sources/StrandDesign/SportIcon.swift +++ b/Packages/StrandDesign/Sources/StrandDesign/SportIcon.swift @@ -598,6 +598,7 @@ public struct WorkoutTypeIcon: View { switch WorkoutTypeIconography.glyph(for: type) { case .system(let name): Image(systemName: name) + .resizable().scaledToFit() .font(.system(size: size, weight: weight)) .symbolRenderingMode(.monochrome) case .custom(let custom): diff --git a/StrandiOSWidgets/TrainingWidget.swift b/StrandiOSWidgets/TrainingWidget.swift index 4ea494e304..67a23b46de 100644 --- a/StrandiOSWidgets/TrainingWidget.swift +++ b/StrandiOSWidgets/TrainingWidget.swift @@ -122,14 +122,16 @@ struct TrainingWidgetView: View { Group { if compact { favoriteTitle(favorite, configured: configured) + .lineLimit(2) } else { - VStack(alignment: .leading, spacing: NoopMetrics.space2) { + VStack(spacing: NoopMetrics.space2) { favoriteIcon(favorite) favoriteTitle(favorite, configured: configured) + .lineLimit(2, reservesSpace: true) } } } - .frame(maxWidth: .infinity, maxHeight: compact ? nil : .infinity, alignment: .leading) + .frame(maxWidth: .infinity, maxHeight: compact ? nil : .infinity) .padding(NoopMetrics.space2) .frame(minHeight: NoopButtonMetrics.minHitTarget) .background(renderingMode == .fullColor ? StrandPalette.surfaceRaised : StrandPalette.hairlineStrong, @@ -147,15 +149,18 @@ struct TrainingWidgetView: View { Group { if configured { Text(favorite.name) } else { Text("Configure") } } - .font(StrandFont.caption.weight(.semibold)).lineLimit(2).minimumScaleFactor(0.8) + .font(StrandFont.caption.weight(.semibold)).minimumScaleFactor(0.8) + .multilineTextAlignment(.center) .foregroundStyle(configured ? textColor : StrandPalette.textTertiary) } @ViewBuilder private func favoriteIcon(_ favorite: TrainingFavorite) -> some View { if let sport = favorite.sport { - WorkoutTypeIcon(workoutType: sport, size: NoopMetrics.space5, color: iconColor) + WorkoutTypeIcon(workoutType: sport, size: NoopMetrics.space6, color: iconColor) } else { Image(systemName: favorite.programID == nil ? "plus" : "dumbbell.fill") + .resizable().scaledToFit() .font(StrandFont.bodyNumber) + .frame(width: NoopMetrics.space6, height: NoopMetrics.space6) .foregroundStyle(favorite.programID == nil ? StrandPalette.textTertiary : iconColor) } } diff --git a/docs/FORK_TRAINING_TESTING.md b/docs/FORK_TRAINING_TESTING.md index cb22390be1..d3de8c804e 100644 --- a/docs/FORK_TRAINING_TESTING.md +++ b/docs/FORK_TRAINING_TESTING.md @@ -64,8 +64,9 @@ Build/test evidence and actual device results should accompany each later upstre ## Widget and Live Activity presentation -The Training widget uses three equal favorite tiles when idle and compact favorite buttons -below the elapsed time and session controls while training. During confirmation or a save +The Training widget uses three equal favorite tiles with centered icons and two-line labels +when idle, and compact favorite buttons below the elapsed time and session controls while +training. During confirmation or a save error, that action gets the available space. Lock Screen controls use distinct pause/resume and stop symbols with complete VoiceOver labels; the Lift completed-set warning remains in the confirmation accessibility hint when the accessory cannot fit the full sentence. @@ -75,6 +76,7 @@ exercise, phase, repetitions/weight, next set and heart rate in a compact layout prioritizes the prompt and saved-set warning. Existing update throttles are unchanged. Workout activities use the app's existing workout-type icon component, including its custom glyphs. The raw sport travels separately from the localized title; older snapshots remain readable. +System workout glyphs fit inside the component's existing square size, preserving their proportions. Build 437 was checked with rendered SwiftUI layouts for idle, active, paused and confirmation states, including long German exercise names, standard iOS font sizes and small widget dimensions. diff --git a/project.yml b/project.yml index 08781cf732..3fb7020bce 100644 --- a/project.yml +++ b/project.yml @@ -21,7 +21,7 @@ settings: # iOS build number (CFBundleVersion). GLOBAL so the iOS app AND its widget extension inherit the # SAME value — they must match or iOS warns "extension version must match parent app" (#416). # macOS sets its own CFBundleVersion on the Strand target and is unaffected by this. - CURRENT_PROJECT_VERSION: "438" + CURRENT_PROJECT_VERSION: "439" SWIFT_VERSION: "5.0" # Emit localizable strings so Xcode auto-extracts every LocalizedStringKey into the # String Catalog on build (the English (US) base entries). diff --git a/scripts/security/baselines/source.json b/scripts/security/baselines/source.json index 8451bc0c44..9ee3021fac 100644 --- a/scripts/security/baselines/source.json +++ b/scripts/security/baselines/source.json @@ -679,7 +679,7 @@ "android/gradlew": "fc977a94723af68aaffa4e5d60496fb4aeed1884b6b19e5e2f2fd7612673313d", "docs/protocol-examples/check_source_references.py": "dce0d33becfc69a42a847ede982799f88f4f6c83ddc34a4a371589d82cc6fc03", "docs/protocol-examples/validate_examples.py": "c94878750e993523d23883934fe81b393f8de3b349f37ef4e5fc34107acbceb5", - "project.yml": "781a62e1847156ada84058c76c3d354fb2651de65315d143fe43d0ca724de596", + "project.yml": "1e23e7a37d482465f1b94679ca7bc2badaa62d534417294ef1aa61e24061a20d", "scripts/security/auto_update.py": "514a480ad272c1159bca0bd2dee76fb15cc9ae57f6426169c9b0348afca48478", "scripts/security/binary.py": "d7f5a39aa0e9745c4ce31b7df103b0361461eb52a695a8031c69644ef2ec1e41", "scripts/security/enable-background-install.py": "1847a9a4bcca0b9e069b5196878aa0227ad4a3131db87958d53e68248b13d0e2",