import SwiftUI import WidgetKit // MARK: - Widget Bundle @main struct KordantWidgets: WidgetBundle { var body: some Widget { ThreatScoreWidget() // systemSmall AlertSummaryWidget() // systemMedium FullDashboardWidget() // systemLarge } } // MARK: - Timeline Provider struct KordantWidgetProvider: IntentTimelineProvider { typealias Entry = WidgetEntry typealias Intent = KordantWidgetConfigurationIntent func placeholder(in context: Context) -> WidgetEntry { WidgetEntry( date: Date(), widgetData: .placeholder, severityFilter: .all, isPlaceholder: true ) } func getSnapshot( for configuration: KordantWidgetConfigurationIntent, in context: Context, completion: @escaping (WidgetEntry) -> Void ) { Task { let data = WidgetDataManager.shared.load() ?? .placeholder let entry = WidgetEntry( date: Date(), widgetData: data, severityFilter: configuration.severityFilter, isPlaceholder: context.isPreview ) completion(entry) } } func getTimeline( for configuration: KordantWidgetConfigurationIntent, in context: Context, completion: @escaping (Timeline) -> Void ) { Task { let data = WidgetDataManager.shared.load() ?? .unavailable let entry = WidgetEntry( date: Date(), widgetData: data, severityFilter: configuration.severityFilter, isPlaceholder: false ) // Widgets must refresh at most every 15 minutes per system policy. let nextRefresh = Calendar.current.date( byAdding: .minute, value: 15, to: Date() ) ?? Date().addingTimeInterval(900) // If data is unavailable, retry sooner (5 minutes) to pick up // initial data after the user opens the app. let refreshDate = data == .unavailable ? Date().addingTimeInterval(300) : nextRefresh let timeline = Timeline(entries: [entry], policy: .after(refreshDate)) completion(timeline) } } } // MARK: - System Small: Threat Score Gauge struct ThreatScoreWidget: Widget { let kind: String = "com.frenocorp.kordant.widget.threatScore" var body: some WidgetConfiguration { IntentConfiguration( kind: kind, intent: KordantWidgetConfigurationIntent.self, provider: KordantWidgetProvider() ) { entry in KordantWidgetsEntryView(entry: entry) .containerBackground(.widgetBackground, for: .widget) } .configurationDisplayName("Threat Score") .description("Your current Kordant threat score at a glance.") .supportedFamilies([.systemSmall]) .contentMarginsDisabled() } } // MARK: - System Medium: Threat Score + Recent Alerts struct AlertSummaryWidget: Widget { let kind: String = "com.frenocorp.kordant.widget.alertSummary" var body: some WidgetConfiguration { IntentConfiguration( kind: kind, intent: KordantWidgetConfigurationIntent.self, provider: KordantWidgetProvider() ) { entry in KordantWidgetsEntryView(entry: entry) .containerBackground(.widgetBackground, for: .widget) } .configurationDisplayName("Alert Summary") .description("Your threat score with up to 2 recent alerts.") .supportedFamilies([.systemMedium]) .contentMarginsDisabled() } } // MARK: - System Large: Full Dashboard struct FullDashboardWidget: Widget { let kind: String = "com.frenocorp.kordant.widget.fullDashboard" var body: some WidgetConfiguration { IntentConfiguration( kind: kind, intent: KordantWidgetConfigurationIntent.self, provider: KordantWidgetProvider() ) { entry in KordantWidgetsEntryView(entry: entry) .containerBackground(.widgetBackground, for: .widget) } .configurationDisplayName("Security Dashboard") .description("Full dashboard with threat score, alerts, stats, and quick actions.") .supportedFamilies([.systemLarge]) .contentMarginsDisabled() } } // MARK: - Entry View Router struct KordantWidgetsEntryView: View { let entry: WidgetEntry @Environment(\.widgetFamily) var family var body: some View { Group { switch family { case .systemSmall: SmallWidgetView(entry: entry) case .systemMedium: MediumWidgetView(entry: entry) case .systemLarge: LargeWidgetView(entry: entry) @unknown default: SmallWidgetView(entry: entry) } } } }