import SwiftUI 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) } }