import SwiftUI enum AppTheme { static let accent = Color(red: 0.12, green: 0.40, blue: 0.31) static let warmAccent = Color(red: 0.84, green: 0.71, blue: 0.48) static let border = Color.black.opacity(0.08) static let shadow = Color.black.opacity(0.05) static let cardFill = Color.white.opacity(0.96) static let mutedFill = Color(red: 0.972, green: 0.976, blue: 0.970) } enum AppLayout { static let compactHorizontalPadding: CGFloat = 16 static let regularHorizontalPadding: CGFloat = 28 static let compactVerticalPadding: CGFloat = 18 static let regularVerticalPadding: CGFloat = 28 static let compactContentWidth: CGFloat = 720 static let regularContentWidth: CGFloat = 920 static let cardRadius: CGFloat = 24 static let largeCardRadius: CGFloat = 30 static let compactSectionPadding: CGFloat = 18 static let regularSectionPadding: CGFloat = 24 static let compactSectionSpacing: CGFloat = 18 static let regularSectionSpacing: CGFloat = 24 static let compactBottomDockPadding: CGFloat = 120 static let regularBottomPadding: CGFloat = 56 static func horizontalPadding(for compactLayout: Bool) -> CGFloat { compactLayout ? compactHorizontalPadding : regularHorizontalPadding } static func verticalPadding(for compactLayout: Bool) -> CGFloat { compactLayout ? compactVerticalPadding : regularVerticalPadding } static func contentWidth(for compactLayout: Bool) -> CGFloat { compactLayout ? compactContentWidth : regularContentWidth } static func sectionPadding(for compactLayout: Bool) -> CGFloat { compactLayout ? compactSectionPadding : regularSectionPadding } static func sectionSpacing(for compactLayout: Bool) -> CGFloat { compactLayout ? compactSectionSpacing : regularSectionSpacing } } extension View { func appSurface(radius: CGFloat = AppLayout.cardRadius, fill: Color = AppTheme.cardFill) -> some View { background( fill, in: RoundedRectangle(cornerRadius: radius, style: .continuous) ) .overlay( RoundedRectangle(cornerRadius: radius, style: .continuous) .stroke(AppTheme.border, lineWidth: 1) ) .shadow(color: AppTheme.shadow, radius: 12, y: 3) } } struct AppBackground: View { var body: some View { LinearGradient( colors: [ Color(red: 0.975, green: 0.978, blue: 0.972), Color.white ], startPoint: .top, endPoint: .bottom ) .overlay(alignment: .top) { Rectangle() .fill(Color.black.opacity(0.02)) .frame(height: 160) .blur(radius: 60) .offset(y: -90) } .ignoresSafeArea() } } struct AppScrollScreen: View { let compactLayout: Bool var bottomPadding: CGFloat? = nil let content: () -> Content init( compactLayout: Bool, bottomPadding: CGFloat? = nil, @ViewBuilder content: @escaping () -> Content ) { self.compactLayout = compactLayout self.bottomPadding = bottomPadding self.content = content } var body: some View { ScrollView { VStack(alignment: .leading, spacing: AppLayout.sectionSpacing(for: compactLayout)) { content() } .frame(maxWidth: AppLayout.contentWidth(for: compactLayout), alignment: .leading) .padding(.horizontal, AppLayout.horizontalPadding(for: compactLayout)) .padding(.top, AppLayout.verticalPadding(for: compactLayout)) .padding(.bottom, bottomPadding ?? AppLayout.verticalPadding(for: compactLayout)) .frame(maxWidth: .infinity, alignment: compactLayout ? .leading : .center) } .scrollIndicators(.hidden) } } struct AppPanel: View { let compactLayout: Bool let radius: CGFloat let content: () -> Content init( compactLayout: Bool, radius: CGFloat = AppLayout.cardRadius, @ViewBuilder content: @escaping () -> Content ) { self.compactLayout = compactLayout self.radius = radius self.content = content } var body: some View { VStack(alignment: .leading, spacing: 14) { content() } .padding(AppLayout.sectionPadding(for: compactLayout)) .frame(maxWidth: .infinity, alignment: .leading) .appSurface(radius: radius) } } struct AppBadge: View { let title: String var tone: Color = AppTheme.accent var body: some View { Text(title) .font(.caption.weight(.semibold)) .foregroundStyle(tone) .padding(.horizontal, 12) .padding(.vertical, 8) .background(tone.opacity(0.10), in: Capsule()) } } struct AppSectionCard: View { let title: String var subtitle: String? = nil let compactLayout: Bool let content: () -> Content init( title: String, subtitle: String? = nil, compactLayout: Bool, @ViewBuilder content: @escaping () -> Content ) { self.title = title self.subtitle = subtitle self.compactLayout = compactLayout self.content = content } var body: some View { AppPanel(compactLayout: compactLayout) { AppSectionTitle(title: title, subtitle: subtitle) content() } } } struct AppSectionTitle: View { let title: String var subtitle: String? = nil var body: some View { VStack(alignment: .leading, spacing: 4) { Text(title) .font(.title3.weight(.semibold)) if let subtitle, !subtitle.isEmpty { Text(subtitle) .font(.subheadline) .foregroundStyle(.secondary) } } } } struct AppNotice: View { let message: String var tone: Color = AppTheme.accent var body: some View { HStack(spacing: 10) { Image(systemName: "checkmark.circle.fill") .font(.footnote.weight(.bold)) .foregroundStyle(tone) Text(message) .font(.subheadline.weight(.semibold)) } .padding(.horizontal, 16) .padding(.vertical, 12) .background(tone.opacity(0.08), in: Capsule()) .overlay( Capsule() .stroke(AppTheme.border, lineWidth: 1) ) } } struct AppStatusTag: View { let title: String var tone: Color = AppTheme.accent var body: some View { Text(title) .font(.caption.weight(.semibold)) .lineLimit(1) .minimumScaleFactor(0.8) .fixedSize(horizontal: true, vertical: false) .padding(.horizontal, 10) .padding(.vertical, 6) .background(tone.opacity(0.12), in: Capsule()) .foregroundStyle(tone) } } struct AppKeyValue: View { let label: String let value: String var monospaced: Bool = false var body: some View { VStack(alignment: .leading, spacing: 4) { Text(label.uppercased()) .font(.caption2.weight(.bold)) .foregroundStyle(.secondary) Text(value) .font(monospaced ? .subheadline.monospaced() : .subheadline.weight(.semibold)) .lineLimit(2) .minimumScaleFactor(0.8) } .frame(maxWidth: .infinity, alignment: .leading) } } struct AppMetric: View { let title: String let value: String var body: some View { VStack(alignment: .leading, spacing: 6) { Text(title.uppercased()) .font(.caption.weight(.bold)) .foregroundStyle(.secondary) Text(value) .font(.title3.weight(.bold)) } .frame(maxWidth: .infinity, alignment: .leading) } } struct AppTextSurface: View { let text: String var monospaced: Bool = false var body: some View { content .frame(maxWidth: .infinity, alignment: .leading) .padding(16) .background(AppTheme.mutedFill, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: 20, style: .continuous) .stroke(AppTheme.border, lineWidth: 1) ) } @ViewBuilder private var content: some View { #if os(watchOS) Text(text) .font(monospaced ? .body.monospaced() : .body) #else Text(text) .font(monospaced ? .body.monospaced() : .body) .textSelection(.enabled) #endif } } struct AppTextEditorField: View { @Binding var text: String var minHeight: CGFloat = 120 var monospaced: Bool = true var body: some View { editor .frame(minHeight: minHeight) .background(AppTheme.mutedFill, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: 20, style: .continuous) .stroke(AppTheme.border, lineWidth: 1) ) } @ViewBuilder private var editor: some View { #if os(watchOS) Text(text) .font(monospaced ? .body.monospaced() : .body) .frame(maxWidth: .infinity, alignment: .leading) .padding(14) #else TextEditor(text: $text) .font(monospaced ? .body.monospaced() : .body) .scrollContentBackground(.hidden) .autocorrectionDisabled() .padding(14) #endif } } struct AppActionRow: View { let title: String var subtitle: String? = nil let systemImage: String var tone: Color = AppTheme.accent var body: some View { HStack(alignment: .top, spacing: 12) { Image(systemName: systemImage) .font(.subheadline.weight(.semibold)) .foregroundStyle(tone) .frame(width: 28, height: 28) VStack(alignment: .leading, spacing: 4) { Text(title) .font(.headline) if let subtitle, !subtitle.isEmpty { Text(subtitle) .font(.subheadline) .foregroundStyle(.secondary) .multilineTextAlignment(.leading) } } Spacer(minLength: 0) Image(systemName: "arrow.right") .font(.footnote.weight(.bold)) .foregroundStyle(.secondary) } .frame(maxWidth: .infinity, alignment: .leading) } } struct AppActionTile: View { let title: String let systemImage: String var tone: Color = AppTheme.accent var isBusy: Bool = false var body: some View { VStack(alignment: .leading, spacing: 14) { HStack(alignment: .center) { ZStack { Circle() .fill(tone.opacity(0.10)) .frame(width: 38, height: 38) if isBusy { ProgressView() .tint(tone) } else { Image(systemName: systemImage) .font(.headline.weight(.semibold)) .foregroundStyle(tone) } } Spacer(minLength: 0) Image(systemName: "arrow.up.right") .font(.caption.weight(.bold)) .foregroundStyle(.secondary) } Text(title) .font(.headline) .multilineTextAlignment(.leading) .lineLimit(2) .frame(maxWidth: .infinity, alignment: .leading) } .padding(16) .frame(maxWidth: .infinity, minHeight: 92, alignment: .topLeading) .appSurface(radius: 22) } }