Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2026-01-31 11:54:14 -06:00
parent 74ece5a71a
commit 7bd01554de
9 changed files with 125 additions and 119 deletions

View File

@ -12,14 +12,25 @@ import Bedrock
struct ContentView: View {
// MARK: - Body
private enum Tab: Hashable {
case clock
case alarms
case noise
case settings
}
@State private var selectedTab: Tab = .clock
@State private var clockViewModel = ClockViewModel()
var body: some View {
TabView {
TabView(selection: $selectedTab) {
NavigationStack {
ClockView()
ClockView(viewModel: clockViewModel)
}
.tabItem {
Label("Clock", systemImage: "clock")
}
.tag(Tab.clock)
NavigationStack {
AlarmView()
@ -27,6 +38,7 @@ struct ContentView: View {
.tabItem {
Label("Alarms", systemImage: "alarm")
}
.tag(Tab.alarms)
NavigationStack {
NoiseView()
@ -34,6 +46,19 @@ struct ContentView: View {
.tabItem {
Label("Noise", systemImage: "waveform")
}
.tag(Tab.noise)
NavigationStack {
ClockSettingsView(style: clockViewModel.style) { newStyle in
clockViewModel.updateStyle(newStyle)
}
.navigationTitle("Settings")
.navigationBarTitleDisplayMode(.inline)
}
.tabItem {
Label("Settings", systemImage: "gearshape")
}
.tag(Tab.settings)
}
.accentColor(AppAccent.primary)
.background(Color.Branding.primary.ignoresSafeArea())

View File

@ -6,6 +6,7 @@
//
import SwiftUI
import Bedrock
/// Main alarm management view
struct AlarmView: View {
@ -26,6 +27,8 @@ struct AlarmView: View {
.onTapGesture {
showAddAlarm = true
}
.frame(maxWidth: Design.Size.maxContentWidthPortrait)
.frame(maxWidth: .infinity, alignment: .center)
} else {
List {
ForEach(viewModel.alarms) { alarm in
@ -43,6 +46,8 @@ struct AlarmView: View {
}
.onDelete(perform: deleteAlarm)
}
.frame(maxWidth: Design.Size.maxContentWidthPortrait)
.frame(maxWidth: .infinity, alignment: .center)
}
}
.navigationTitle("Alarms")

View File

@ -354,7 +354,7 @@ class ClockStyle: Codable, Equatable {
// Color-aware brightness adaptation
let colorAwareBrightness = getColorAwareBrightness()
Design.debugLog("[brightness] effectiveBrightness: Color-aware brightness = \(String(format: \"%.2f\", colorAwareBrightness))")
Design.debugLog("[brightness] effectiveBrightness: Color-aware brightness = \(String(format: "%.2f", colorAwareBrightness))")
return colorAwareBrightness
}

View File

@ -61,15 +61,15 @@ class AmbientLightService {
let previousBrightness = UIScreen.main.brightness
Design.debugLog("[ambient] AmbientLightService.setBrightness:")
Design.debugLog("[ambient] - Requested brightness: \(String(format: \"%.2f\", brightness))")
Design.debugLog("[ambient] - Clamped brightness: \(String(format: \"%.2f\", clampedBrightness))")
Design.debugLog("[ambient] - Previous screen brightness: \(String(format: \"%.2f\", previousBrightness))")
Design.debugLog("[ambient] - Requested brightness: \(String(format: "%.2f", brightness))")
Design.debugLog("[ambient] - Clamped brightness: \(String(format: "%.2f", clampedBrightness))")
Design.debugLog("[ambient] - Previous screen brightness: \(String(format: "%.2f", previousBrightness))")
UIScreen.main.brightness = clampedBrightness
currentBrightness = clampedBrightness
Design.debugLog("[ambient] - New screen brightness: \(String(format: \"%.2f\", UIScreen.main.brightness))")
Design.debugLog("[ambient] - Service currentBrightness: \(String(format: \"%.2f\", currentBrightness))")
Design.debugLog("[ambient] - New screen brightness: \(String(format: "%.2f", UIScreen.main.brightness))")
Design.debugLog("[ambient] - Service currentBrightness: \(String(format: "%.2f", currentBrightness))")
}
/// Get current screen brightness
@ -90,7 +90,7 @@ class AmbientLightService {
let previousBrightness = currentBrightness
currentBrightness = newBrightness
Design.debugLog("[ambient] AmbientLightService: Brightness changed from \(String(format: \"%.2f\", previousBrightness)) to \(String(format: \"%.2f\", newBrightness))")
Design.debugLog("[ambient] AmbientLightService: Brightness changed from \(String(format: "%.2f", previousBrightness)) to \(String(format: "%.2f", newBrightness))")
// Notify that brightness changed
onBrightnessChange?()

View File

@ -221,16 +221,16 @@ class ClockViewModel {
Design.debugLog("[brightness] Auto Brightness Debug:")
Design.debugLog("[brightness] - Auto brightness enabled: \(style.autoBrightness)")
Design.debugLog("[brightness] - Current screen brightness: \(String(format: \"%.2f\", currentScreenBrightness))")
Design.debugLog("[brightness] - Target brightness: \(String(format: \"%.2f\", targetBrightness))")
Design.debugLog("[brightness] - Current screen brightness: \(String(format: "%.2f", currentScreenBrightness))")
Design.debugLog("[brightness] - Target brightness: \(String(format: "%.2f", targetBrightness))")
Design.debugLog("[brightness] - Night mode active: \(isNightMode)")
Design.debugLog("[brightness] - Color theme: \(style.selectedColorTheme)")
Design.debugLog("[brightness] - Ambient light threshold: \(String(format: \"%.2f\", style.ambientLightThreshold))")
Design.debugLog("[brightness] - Ambient light threshold: \(String(format: "%.2f", style.ambientLightThreshold))")
ambientLightService.setBrightness(targetBrightness)
Design.debugLog("[brightness] - Brightness set to: \(String(format: \"%.2f\", targetBrightness))")
Design.debugLog("[brightness] - Actual screen brightness now: \(String(format: \"%.2f\", UIScreen.main.brightness))")
Design.debugLog("[brightness] - Brightness set to: \(String(format: "%.2f", targetBrightness))")
Design.debugLog("[brightness] - Actual screen brightness now: \(String(format: "%.2f", UIScreen.main.brightness))")
Design.debugLog("[brightness] ---")
} else {
Design.debugLog("[brightness] Auto Brightness: DISABLED")

View File

@ -18,7 +18,6 @@ struct ClockSettingsView: View {
@State private var digitColor: Color = .white
@State private var backgroundColor: Color = .black
@State private var showAdvancedSettings = false
@Environment(\.dismiss) private var dismiss
// MARK: - Init
init(style: ClockStyle, onCommit: @escaping (ClockStyle) -> Void) {
@ -28,94 +27,88 @@ struct ClockSettingsView: View {
// MARK: - Body
var body: some View {
NavigationStack {
ScrollView {
VStack(spacing: Design.Spacing.xxLarge) {
BasicAppearanceSection(
style: $style,
digitColor: $digitColor,
backgroundColor: $backgroundColor
)
ScrollView {
VStack(spacing: Design.Spacing.xxLarge) {
BasicAppearanceSection(
style: $style,
digitColor: $digitColor,
backgroundColor: $backgroundColor
)
BasicDisplaySection(style: $style)
BasicDisplaySection(style: $style)
if showAdvancedSettings {
AdvancedAppearanceSection(style: $style)
if showAdvancedSettings {
AdvancedAppearanceSection(style: $style)
FontSection(style: $style)
FontSection(style: $style)
NightModeSection(style: $style)
NightModeSection(style: $style)
OverlaySection(style: $style)
OverlaySection(style: $style)
AdvancedDisplaySection(style: $style)
}
AdvancedDisplaySection(style: $style)
}
SettingsSectionHeader(
title: "Advanced",
systemImage: "gearshape",
SettingsSectionHeader(
title: "Advanced",
systemImage: "gearshape",
accentColor: AppAccent.primary
)
SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) {
SettingsToggle(
title: "Show Advanced Settings",
subtitle: "Reveal additional customization options",
isOn: $showAdvancedSettings,
accentColor: AppAccent.primary
)
}
SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) {
SettingsToggle(
title: "Show Advanced Settings",
subtitle: "Reveal additional customization options",
isOn: $showAdvancedSettings,
accentColor: AppAccent.primary
#if DEBUG
SettingsSectionHeader(
title: "Debug",
systemImage: "ant.fill",
accentColor: AppStatus.error
)
SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) {
SettingsNavigationRow(
title: "Icon Generator",
subtitle: "Generate and save app icon",
backgroundColor: AppSurface.primary
) {
IconGeneratorView(config: .noiseClock, appName: "TheNoiseClock")
}
SettingsNavigationRow(
title: "Branding Preview",
subtitle: "Preview icon and launch screen",
backgroundColor: AppSurface.primary
) {
BrandingPreviewView(
iconConfig: .noiseClock,
launchConfig: .noiseClock,
appName: "TheNoiseClock"
)
}
#if DEBUG
SettingsSectionHeader(
title: "Debug",
systemImage: "ant.fill",
accentColor: AppStatus.error
)
SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) {
SettingsNavigationRow(
title: "Icon Generator",
subtitle: "Generate and save app icon",
backgroundColor: AppSurface.primary
) {
IconGeneratorView(config: .noiseClock, appName: "TheNoiseClock")
}
SettingsNavigationRow(
title: "Branding Preview",
subtitle: "Preview icon and launch screen",
backgroundColor: AppSurface.primary
) {
BrandingPreviewView(
iconConfig: .noiseClock,
launchConfig: .noiseClock,
appName: "TheNoiseClock"
)
}
}
#endif
}
.padding(.horizontal, Design.Spacing.large)
.padding(.top, Design.Spacing.large)
.padding(.bottom, Design.Spacing.xxxLarge)
}
.background(AppSurface.primary)
.navigationTitle("Clock Settings")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Done") { dismiss() }
.foregroundStyle(AppAccent.primary)
}
}
.onAppear {
digitColor = Color(hex: style.digitColorHex) ?? .white
backgroundColor = Color(hex: style.backgroundHex) ?? .black
}
.onDisappear {
onCommit(style)
#endif
}
.frame(maxWidth: Design.Size.maxContentWidthPortrait)
.frame(maxWidth: .infinity, alignment: .center)
.padding(.horizontal, Design.Spacing.large)
.padding(.top, Design.Spacing.large)
.padding(.bottom, Design.Spacing.xxxLarge)
}
.background(AppSurface.primary)
.navigationTitle("Clock Settings")
.navigationBarTitleDisplayMode(.inline)
.onAppear {
digitColor = Color(hex: style.digitColorHex) ?? .white
backgroundColor = Color(hex: style.backgroundHex) ?? .black
}
.onDisappear {
onCommit(style)
}
}
}

View File

@ -12,8 +12,7 @@ import Bedrock
struct ClockView: View {
// MARK: - Properties
@State private var viewModel = ClockViewModel()
@State private var showSettings = false
@Bindable var viewModel: ClockViewModel
@State private var showFullScreenHint = false
// MARK: - Body
@ -43,20 +42,10 @@ struct ClockView: View {
}
.ignoresSafeArea(.all, edges: viewModel.isDisplayMode ? .bottom : [])
.statusBarHidden(viewModel.isDisplayMode)
.sheet(isPresented: $showSettings) {
ClockSettingsView(style: viewModel.style) { newStyle in
viewModel.updateStyle(newStyle)
}
.presentationDetents([.large])
.presentationDragIndicator(.hidden)
.presentationBackgroundInteraction(.enabled)
.presentationBackground(AppSurface.overlay)
}
.overlay {
// Toolbar overlay
ClockToolbar(
isDisplayMode: viewModel.isDisplayMode,
onSettingsTap: { showSettings = true },
onFullScreenTap: {
if !viewModel.isDisplayMode {
viewModel.toggleDisplayMode()
@ -87,7 +76,7 @@ struct ClockView: View {
// MARK: - Preview
#Preview {
NavigationStack {
ClockView()
ClockView(viewModel: ClockViewModel())
}
.frame(width: 400, height: 600)
.background(Color.black)

View File

@ -12,7 +12,6 @@ struct ClockToolbar: View {
// MARK: - Properties
let isDisplayMode: Bool
let onSettingsTap: () -> Void
let onFullScreenTap: () -> Void
// MARK: - Body
@ -30,15 +29,6 @@ struct ClockToolbar: View {
.transition(.opacity)
}
.accessibilityLabel("Enter Full Screen Mode")
Button {
onSettingsTap()
} label: {
Image(systemName: "gear")
.font(.title2)
.transition(.opacity)
}
.accessibilityLabel("Clock Settings")
}
}
}
@ -52,7 +42,6 @@ struct ClockToolbar: View {
NavigationStack {
ClockToolbar(
isDisplayMode: false,
onSettingsTap: {},
onFullScreenTap: {}
)
}

View File

@ -27,14 +27,19 @@ struct NoiseView: View {
var body: some View {
GeometryReader { geometry in
let isLandscape = geometry.size.width > geometry.size.height
let maxWidth = isLandscape ? Design.Size.maxContentWidthLandscape : Design.Size.maxContentWidthPortrait
if isLandscape {
// Landscape layout: Player on left, sounds on right
landscapeLayout
} else {
// Portrait layout: Stacked vertically
portraitLayout
Group {
if isLandscape {
// Landscape layout: Player on left, sounds on right
landscapeLayout
} else {
// Portrait layout: Stacked vertically
portraitLayout
}
}
.frame(maxWidth: maxWidth)
.frame(maxWidth: .infinity, alignment: .center)
}
.animation(.easeInOut(duration: 0.3), value: selectedSound)
}