diff --git a/README.md b/README.md index afd7b6c..9d5bbee 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A minimal macOS menu bar timer with task tracking. ## Requirements -- macOS 15.0 (Sequoia) or later +- macOS 13.0 (Ventura) or later - Xcode 16+ (for building from source) ## Installation diff --git a/musubi.xcodeproj/project.pbxproj b/musubi.xcodeproj/project.pbxproj index d13b223..cc1012e 100644 --- a/musubi.xcodeproj/project.pbxproj +++ b/musubi.xcodeproj/project.pbxproj @@ -177,7 +177,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 15.6; + MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -234,7 +234,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 15.6; + MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; diff --git a/musubi/Assets.xcassets/AppIcon.appiconset/Contents.json b/musubi/Assets.xcassets/AppIcon.appiconset/Contents.json index 3f00db4..7d9d04a 100644 --- a/musubi/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/musubi/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -46,6 +46,7 @@ "size" : "512x512" }, { + "filename" : "icon-1765872958423.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" diff --git a/musubi/Assets.xcassets/AppIcon.appiconset/icon-1765872958423.png b/musubi/Assets.xcassets/AppIcon.appiconset/icon-1765872958423.png new file mode 100644 index 0000000..f692145 Binary files /dev/null and b/musubi/Assets.xcassets/AppIcon.appiconset/icon-1765872958423.png differ diff --git a/musubi/Assets.xcassets/MusubiAppIcon-iOS-Default-1024x1024@1x.png b/musubi/Assets.xcassets/MusubiAppIcon-iOS-Default-1024x1024@1x.png new file mode 100644 index 0000000..f56a5a0 Binary files /dev/null and b/musubi/Assets.xcassets/MusubiAppIcon-iOS-Default-1024x1024@1x.png differ diff --git a/musubi/UIHelpers/RulerSlider.swift b/musubi/UIHelpers/RulerSlider.swift index 3f7a18e..2c1f9c0 100644 --- a/musubi/UIHelpers/RulerSlider.swift +++ b/musubi/UIHelpers/RulerSlider.swift @@ -23,6 +23,15 @@ struct RulerSlider: View { @State private var scrolledID: Int? var body: some View { + if #available(macOS 14.0, *) { + modernRulerSlider + } else { + legacyRulerSlider + } + } + + @available(macOS 14.0, *) + private var modernRulerSlider: some View { GeometryReader { geometry in let halfWidth = geometry.size.width / 2 @@ -61,8 +70,49 @@ struct RulerSlider: View { } .frame(height: totalHeight) } + + private var legacyRulerSlider: some View { + GeometryReader { geometry in + let halfWidth = geometry.size.width / 2 + + ScrollViewReader { proxy in + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: tickSpacing) { + ForEach(range, id: \.self) { minute in + TickMark( + minute: minute, + isMajor: minute % 10 == 0, + isSelected: minute == value, + accentColor: accentColor, + minorHeight: minorTickHeight, + majorHeight: majorTickHeight + ) + .id(minute) + .onTapGesture { + value = minute + withAnimation { + proxy.scrollTo(minute, anchor: .center) + } + } + } + } + .padding(.horizontal, halfWidth) + } + .onAppear { + proxy.scrollTo(value, anchor: .center) + } + .onChange(of: value) { newValue in + withAnimation { + proxy.scrollTo(newValue, anchor: .center) + } + } + } + } + .frame(height: totalHeight) + } } +@available(macOS 14.0, *) private struct NearestTickBehavior: ScrollTargetBehavior { let tickStep: CGFloat let tickCount: Int @@ -106,6 +156,7 @@ private struct TickMark: View { } } +@available(macOS 14.0, *) #Preview { @Previewable @State var minutes = 25 VStack { diff --git a/musubi/View/ContentView.swift b/musubi/View/ContentView.swift index 12ed628..5e362db 100644 --- a/musubi/View/ContentView.swift +++ b/musubi/View/ContentView.swift @@ -18,7 +18,7 @@ struct ContentView: View { var body: some View { VStack(alignment: .leading) { RulerSlider(value: $sliderMinutes, range: 0...120, accentColor: settings.accentColor) - .onChange(of: sliderMinutes) { oldValue, newValue in + .onChange(of: sliderMinutes) { newValue in vm.setDuration(minutes: newValue) } diff --git a/musubi/View/SettingsView.swift b/musubi/View/SettingsView.swift index ba20dbd..bb5f18b 100644 --- a/musubi/View/SettingsView.swift +++ b/musubi/View/SettingsView.swift @@ -54,7 +54,7 @@ struct SettingsView: View { .foregroundColor(.secondary) Slider(value: $settings.alarmVolume, in: 0...1) .tint(.gray.opacity(0.5)) - .onChange(of: settings.alarmVolume) { _, newValue in + .onChange(of: settings.alarmVolume) { newValue in playPreviewSound(volume: newValue) } Image(systemName: "speaker.wave.3.fill")