import SwiftUI import CoreText enum WatchTheme { static func font(_ size: CGFloat, weight: Font.Weight = .regular) -> Font { .custom("Inter-Regular", size: size).weight(weight) } static func registerFonts() { guard let url = Bundle.main.url(forResource: "Inter", withExtension: "ttf") else { return } CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil) } static let background = Color(hex: 0x0C141F) static let night = Color(hex: 0x0A1019) static let card = Color(hex: 0x151C27) static let accent = Color("AccentColor") static let mint = Color(hex: 0x79D3FF) static let secondary = Color(hex: 0xBEC8CF) static let text = Color(hex: 0xDCE3F3) static let purple = Color(hex: 0xA78BFA) static let pink = Color(hex: 0xE94697) static let stop = Color(hex: 0xFFB4AB) } private extension Color { init(hex: UInt32) { self.init(red: Double((hex >> 16) & 255) / 255, green: Double((hex >> 8) & 255) / 255, blue: Double(hex & 255) / 255) } } struct WatchCard: View { @ViewBuilder var content: Content var body: some View { content.padding(12).frame(maxWidth: .infinity, alignment: .leading) .background(WatchTheme.card, in: RoundedRectangle(cornerRadius: 12)) .overlay(RoundedRectangle(cornerRadius: 12).stroke(.white.opacity(0.08))) } } struct WatchPillStyle: ButtonStyle { var primary = false var destructive = false var height: CGFloat = 36 var fontSize: CGFloat = 14 func makeBody(configuration: Configuration) -> some View { configuration.label .font(destructive ? .system(size: fontSize) : WatchTheme.font(fontSize, weight: .semibold)) .foregroundStyle(destructive ? WatchTheme.stop : primary ? Color(red: 0, green: 0.208, blue: 0.282) : WatchTheme.text) .frame(maxWidth: .infinity).frame(height: height) .background(primary ? WatchTheme.accent : destructive ? .clear : Color(red: 0.18, green: 0.208, blue: 0.255), in: Capsule()) .overlay(Capsule().stroke(destructive ? WatchTheme.stop.opacity(0.3) : .clear)) .opacity(configuration.isPressed ? 0.65 : 1) .contentShape(Capsule()) } } struct WatchMenuRow: View { let title: String var color: Color = WatchTheme.accent var body: some View { HStack { Text(title).font(WatchTheme.font(16, weight: .medium)).fixedSize(horizontal: false, vertical: true) Spacer(minLength: 4) Image("WatchChevron").resizable().scaledToFit().frame(width: 5, height: 8).accessibilityHidden(true) } .foregroundStyle(WatchTheme.text) .padding(.horizontal, 15).frame(minHeight: 50) .background(LinearGradient(colors: [color.opacity(0.1), WatchTheme.card], startPoint: .leading, endPoint: .trailing)) .overlay(alignment: .leading) { Rectangle().fill(color).frame(width: 4) } .clipShape(RoundedRectangle(cornerRadius: 12)) } } struct WatchGlow: View { var color: Color = WatchTheme.accent var body: some View { Circle() .fill(RadialGradient(colors: [color.opacity(0.24), color.opacity(0)], center: .center, startRadius: 0, endRadius: 100)) .frame(width: 210, height: 210) .allowsHitTesting(false).accessibilityHidden(true) } } struct WatchEyebrow: View { let title: String var body: some View { Text(title.uppercased()).font(WatchTheme.font(12, weight: .semibold)) .tracking(0.6).foregroundStyle(WatchTheme.secondary).multilineTextAlignment(.center) .lineLimit(1).minimumScaleFactor(0.7).fixedSize(horizontal: false, vertical: true) } } func watchTime(_ seconds: TimeInterval) -> String { let value = max(0, Int(seconds.rounded(.up))) return String(format: "%02d:%02d", value / 60, value % 60) } extension View { func haptikaScreen() -> some View { scrollContentBackground(.hidden) .background(WatchTheme.background.ignoresSafeArea()) .foregroundStyle(WatchTheme.text) .tint(WatchTheme.accent) } } // Explicit Debug-only presentation fixtures. Never saved or sent to HealthKit. enum WatchDesignMode { static var enabled: Bool { #if DEBUG && targetEnvironment(simulator) ProcessInfo.processInfo.arguments.contains("--figma-preview") #else false #endif } static var initialScreen: WatchFigmaScreen? { guard enabled, let index = ProcessInfo.processInfo.arguments.firstIndex(of: "--figma-screen"), ProcessInfo.processInfo.arguments.indices.contains(index + 1) else { return nil } return WatchFigmaScreen(rawValue: ProcessInfo.processInfo.arguments[index + 1]) } static var frozen: Bool { initialScreen != nil } } struct WatchFigmaCanvas: View { var background: Color = WatchTheme.background @Environment(\.dismiss) private var dismiss @ViewBuilder var content: Content var body: some View { GeometryReader { geometry in content.frame(width: geometry.size.width, height: geometry.size.height) } .scrollContentBackground(.hidden) .foregroundStyle(WatchTheme.text) .tint(WatchTheme.accent) .background(background.ignoresSafeArea()) .toolbar(.hidden, for: .navigationBar) .persistentSystemOverlays(.hidden) .accessibilityAction(.escape) { dismiss() } } } struct WatchBalanceAxis: View { var compact = false var position: Double? var body: some View { HStack(spacing: 4) { if compact { Circle().fill(Color(red: 0.957, green: 0.247, blue: 0.369)).frame(width: 6, height: 6) } GeometryReader { geometry in Capsule().fill(LinearGradient(colors: [compact ? WatchTheme.accent.opacity(0) : Color(red: 0.957, green: 0.247, blue: 0.369), WatchTheme.accent], startPoint: .leading, endPoint: .trailing)) if !compact, let position { Capsule().fill(WatchTheme.text).frame(width: 4, height: 14) .position(x: geometry.size.width * min(1, max(0, position)), y: geometry.size.height / 2) .shadow(color: .white.opacity(0.5), radius: 4) } }.frame(width: compact ? 32 : 120, height: compact ? 4 : 6) if compact { Circle().fill(WatchTheme.accent).frame(width: 6, height: 6) } }.accessibilityHidden(true) } }