import SwiftUI // MARK: - Title Goal Picker struct TitleGoalPicker: View { @Binding var selectedGoal: TitleGoal var body: some View { LazyVGrid( columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 8 ) { ForEach(TitleGoal.allCases) { goal in Button { selectedGoal = goal } label: { VStack(alignment: .leading, spacing: 2) { Text(goal.title) .font(.system(size: 11, weight: .semibold)) .foregroundStyle(AppTheme.textPrimary) Text(goal.subtitle) .font(.system(size: 9)) .foregroundStyle(AppTheme.textTertiary) .lineLimit(1) } .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 10) .padding(.vertical, 8) .background( RoundedRectangle(cornerRadius: 8) .fill( selectedGoal == goal ? AppTheme.accentBlue.opacity(0.15) : AppTheme.panelBackground ) ) .overlay( RoundedRectangle(cornerRadius: 8) .stroke( selectedGoal == goal ? AppTheme.accentBlue.opacity(0.5) : AppTheme.cardBorder, lineWidth: 1 ) ) } .buttonStyle(AppPlainButtonStyle()) .hoverOverlay(cornerRadius: 8) } } } } // MARK: - Variant Count Picker struct TitleVariantCountPicker: View { @Binding var selectedCount: TitleVariantCount var body: some View { HStack(spacing: 8) { ForEach(TitleVariantCount.allCases) { count in Button { selectedCount = count } label: { Text(count.label) .font(.system(size: 10, weight: .semibold)) .foregroundStyle(selectedCount == count ? .white : AppTheme.textSecondary) .frame(maxWidth: .infinity) .padding(.vertical, 7) .background( RoundedRectangle(cornerRadius: 7) .fill( selectedCount == count ? AppTheme.accentBlue : AppTheme.panelBackground ) ) .overlay( RoundedRectangle(cornerRadius: 7) .stroke(AppTheme.cardBorder, lineWidth: 1) ) } .buttonStyle(AppPlainButtonStyle()) .hoverOverlay(cornerRadius: 8) } } } } // MARK: - Title Analysis Card struct TitleAnalysisCard: View { let analysis: TitleAnalysis var body: some View { VStack(alignment: .leading, spacing: 14) { HStack { VStack(alignment: .leading, spacing: 2) { Text("Title Analysis") .font(.system(size: 12, weight: .semibold)) .foregroundStyle(AppTheme.textPrimary) Text("How your current title performs") .font(.system(size: 10)) .foregroundStyle(AppTheme.textSecondary) } Spacer() TitleScoreBadge(score: analysis.overallScore, size: .large) } HStack(spacing: 12) { scoreMetric(label: "Length", score: analysis.lengthScore) scoreMetric(label: "Engagement", score: analysis.engagementScore) scoreMetric(label: "Clarity", score: analysis.clarityScore) } if !analysis.suggestions.isEmpty { VStack(alignment: .leading, spacing: 6) { Text("Suggestions") .font(.system(size: 10, weight: .medium)) .foregroundStyle(AppTheme.textTertiary) ForEach(analysis.suggestions, id: \.self) { suggestion in HStack(alignment: .top, spacing: 6) { Image(systemName: "lightbulb.fill") .font(.system(size: 9)) .foregroundStyle(AppTheme.accentYellow) .padding(.top, 2) Text(suggestion) .font(.system(size: 10)) .foregroundStyle(AppTheme.textSecondary) .fixedSize(horizontal: false, vertical: true) } } } } } .padding(14) .frame(maxWidth: .infinity, alignment: .leading) .background( RoundedRectangle(cornerRadius: AppTheme.cornerRadiusSmall) .fill(AppTheme.cardBackground) .overlay( RoundedRectangle(cornerRadius: AppTheme.cornerRadiusSmall) .stroke(AppTheme.cardBorder, lineWidth: 1) ) ) } private func scoreMetric(label: String, score: Int) -> some View { VStack(spacing: 4) { TitleScoreBadge(score: score, size: .small) Text(label) .font(.system(size: 9, weight: .medium)) .foregroundStyle(AppTheme.textTertiary) } .frame(maxWidth: .infinity) } } // MARK: - Score Badge struct TitleScoreBadge: View { enum Size { case small case large var fontSize: CGFloat { switch self { case .small: 11 case .large: 18 } } var padding: CGFloat { switch self { case .small: 6 case .large: 10 } } } let score: Int var size: Size = .small var body: some View { Text("\(score)") .font(.system(size: size.fontSize, weight: .bold, design: .rounded)) .foregroundStyle(scoreColor) .padding(size.padding) .background( Circle() .fill(scoreColor.opacity(0.15)) ) .overlay( Circle() .stroke(scoreColor.opacity(0.3), lineWidth: 1) ) } private var scoreColor: Color { switch score { case 80...100: AppTheme.accentGreen case 60..<80: AppTheme.accentYellow case 40..<60: AppTheme.accentOrange default: Color(hex: 0xEF4444) } } } // MARK: - Title Variant Row struct TitleVariantRow: View { let variant: TitleVariant let isSelected: Bool let onApply: () -> Void var body: some View { HStack(alignment: .top, spacing: 12) { Button(action: onApply) { Circle() .stroke(isSelected ? AppTheme.accentBlue : AppTheme.cardBorder, lineWidth: 2) .frame(width: 16, height: 16) .overlay { if isSelected { Circle() .fill(AppTheme.accentBlue) .frame(width: 8, height: 8) } } } .buttonStyle(AppPlainButtonStyle()) .hoverOverlay(cornerRadius: 8) .padding(.top, 2) VStack(alignment: .leading, spacing: 6) { HStack(alignment: .top) { Text(variant.title) .font(.system(size: 12, weight: .medium)) .foregroundStyle(AppTheme.textPrimary) .fixedSize(horizontal: false, vertical: true) Spacer(minLength: 8) TitleScoreBadge(score: variant.score) } Text(variant.reasoning) .font(.system(size: 10)) .foregroundStyle(AppTheme.textTertiary) HStack(spacing: 8) { Text("\(variant.title.count) chars") .font(.system(size: 9)) .foregroundStyle( variant.title.count > 300 ? Color(hex: 0xF87171) : AppTheme.textTertiary ) Spacer() Button(action: onApply) { Text("Use Title") .font(.system(size: 10, weight: .semibold)) .foregroundStyle(AppTheme.accentBlue) } .buttonStyle(AppPlainButtonStyle()) .hoverOverlay(cornerRadius: 8) } } } .padding(12) .background( RoundedRectangle(cornerRadius: 8) .fill(isSelected ? AppTheme.accentBlue.opacity(0.08) : AppTheme.panelBackground) ) .overlay( RoundedRectangle(cornerRadius: 8) .stroke( isSelected ? AppTheme.accentBlue.opacity(0.4) : AppTheme.cardBorder, lineWidth: 1 ) ) .hoverCard(cornerRadius: 8, isSelected: isSelected) .hoverCursor() .onTapGesture(perform: onApply) } } // MARK: - Compare Card struct TitleCompareCard: View { let title: String let score: Int? let label: String let isOriginal: Bool var isSelected: Bool = false var body: some View { VStack(alignment: .leading, spacing: 10) { HStack { Text(label) .font(.system(size: 10, weight: .semibold)) .foregroundStyle(isOriginal ? AppTheme.textSecondary : AppTheme.accentBlue) .padding(.horizontal, 8) .padding(.vertical, 3) .background( Capsule() .fill( isOriginal ? AppTheme.panelBackground : AppTheme.accentBlue.opacity(0.15) ) ) Spacer() if let score { TitleScoreBadge(score: score) } } Text(title.isEmpty ? "No title" : title) .font(.system(size: 13, weight: .semibold)) .foregroundStyle(title.isEmpty ? AppTheme.textTertiary : AppTheme.textPrimary) .fixedSize(horizontal: false, vertical: true) HStack { Text("\(title.count) / 300 characters") .font(.system(size: 9)) .foregroundStyle( title.count > 300 ? Color(hex: 0xF87171) : AppTheme.textTertiary ) Spacer() if isSelected { Label("Active", systemImage: "checkmark.circle.fill") .font(.system(size: 9, weight: .semibold)) .foregroundStyle(AppTheme.accentGreen) } } } .padding(14) .frame(maxWidth: .infinity, alignment: .leading) .background( RoundedRectangle(cornerRadius: AppTheme.cornerRadiusSmall) .fill(AppTheme.cardBackground) .overlay( RoundedRectangle(cornerRadius: AppTheme.cornerRadiusSmall) .stroke( isSelected ? AppTheme.accentBlue.opacity(0.5) : AppTheme.cardBorder, lineWidth: isSelected ? 1.5 : 1 ) ) ) .hoverCard(cornerRadius: AppTheme.cornerRadiusSmall, isSelected: isSelected) .hoverCursor() } } // MARK: - Feed Preview Row struct TitleFeedPreviewRow: View { let title: String let subreddit: String var isHighlighted: Bool = false var body: some View { HStack(alignment: .top, spacing: 10) { RoundedRectangle(cornerRadius: 4) .fill(AppTheme.cardBackground) .frame(width: 72, height: 52) .overlay { Image(systemName: "photo") .font(.system(size: 16)) .foregroundStyle(AppTheme.textTertiary) } VStack(alignment: .leading, spacing: 4) { Text(subreddit) .font(.system(size: 9, weight: .semibold)) .foregroundStyle(AppTheme.textTertiary) Text(title.isEmpty ? "Title preview" : title) .font(.system(size: 11, weight: .semibold)) .foregroundStyle(title.isEmpty ? AppTheme.textTertiary : AppTheme.textPrimary) .lineLimit(2) .fixedSize(horizontal: false, vertical: true) } Spacer(minLength: 0) } .padding(10) .background( RoundedRectangle(cornerRadius: 8) .fill(isHighlighted ? AppTheme.accentBlue.opacity(0.08) : AppTheme.panelBackground) ) .overlay( RoundedRectangle(cornerRadius: 8) .stroke( isHighlighted ? AppTheme.accentBlue.opacity(0.4) : AppTheme.cardBorder, lineWidth: 1 ) ) } } // MARK: - Empty State struct TitleOptimizerEmptyState: View { let icon: String let title: String let subtitle: String var body: some View { VStack(spacing: 10) { Image(systemName: icon) .font(.system(size: 28)) .foregroundStyle(AppTheme.accentBlue.opacity(0.6)) Text(title) .font(.system(size: 12, weight: .semibold)) .foregroundStyle(AppTheme.textSecondary) Text(subtitle) .font(.system(size: 10)) .foregroundStyle(AppTheme.textTertiary) .multilineTextAlignment(.center) } .frame(maxWidth: .infinity) .padding(.vertical, 32) .background( RoundedRectangle(cornerRadius: 8) .fill(AppTheme.panelBackground) .overlay( RoundedRectangle(cornerRadius: 8) .strokeBorder( AppTheme.accentBlue.opacity(0.2), style: StrokeStyle(lineWidth: 1, dash: [6, 4]) ) ) ) } }