import SwiftUI #if os(macOS) import AppKit #elseif canImport(UIKit) import UIKit #endif public enum IdP { public static let tint = Color("IdPTint") public static let cardRadius: CGFloat = 12 public static let controlRadius: CGFloat = 8 public static let badgeRadius: CGFloat = 999 static func horizontalPadding(compact: Bool) -> CGFloat { compact ? 16 : 24 } static func verticalPadding(compact: Bool) -> CGFloat { compact ? 16 : 24 } } private enum ShadcnHex { // Light static let bg = Color(red: 1, green: 1, blue: 1) static let fg = Color(red: 0.039, green: 0.039, blue: 0.043) static let muted = Color(red: 0.957, green: 0.957, blue: 0.961) static let mutedFg = Color(red: 0.443, green: 0.443, blue: 0.478) static let border = Color(red: 0.894, green: 0.894, blue: 0.906) static let card = Color(red: 1, green: 1, blue: 1) static let primary = Color(red: 0.094, green: 0.094, blue: 0.106) static let primaryFg = Color(red: 0.980, green: 0.980, blue: 0.980) static let accentSoft = Color(red: 0.961, green: 0.953, blue: 1.0) static let destructive = Color(red: 0.937, green: 0.267, blue: 0.267) static let ok = Color(red: 0.086, green: 0.639, blue: 0.290) static let warn = Color(red: 0.918, green: 0.702, blue: 0.031) // Dark static let bgDark = Color(red: 0.035, green: 0.035, blue: 0.043) static let fgDark = Color(red: 0.980, green: 0.980, blue: 0.980) static let mutedDark = Color(red: 0.094, green: 0.094, blue: 0.106) static let mutedFgDark = Color(red: 0.631, green: 0.631, blue: 0.667) static let borderDark = Color(red: 0.153, green: 0.153, blue: 0.165) static let cardDark = Color(red: 0.047, green: 0.047, blue: 0.059) static let primaryDark = Color(red: 0.980, green: 0.980, blue: 0.980) static let primaryFgDark = Color(red: 0.094, green: 0.094, blue: 0.106) static let accentSoftDark = Color(red: 0.102, green: 0.086, blue: 0.180) static let destructiveDark = Color(red: 0.498, green: 0.114, blue: 0.114) } private extension Color { static func idpAdaptive(light: Color, dark: Color) -> Color { #if os(macOS) Color(nsColor: NSColor(name: nil) { appearance in let matched = appearance.bestMatch(from: [.darkAqua, .vibrantDark, .aqua, .vibrantLight]) let isDark = matched == .darkAqua || matched == .vibrantDark return NSColor(isDark ? dark : light) }) #elseif canImport(UIKit) && !os(watchOS) Color(uiColor: UIColor { traits in UIColor(traits.userInterfaceStyle == .dark ? dark : light) }) #elseif os(watchOS) dark #else light #endif } } extension Color { static let idpBackground = Color.idpAdaptive(light: ShadcnHex.bg, dark: ShadcnHex.bgDark) static let idpForeground = Color.idpAdaptive(light: ShadcnHex.fg, dark: ShadcnHex.fgDark) static let idpMuted = Color.idpAdaptive(light: ShadcnHex.muted, dark: ShadcnHex.mutedDark) static let idpMutedForeground = Color.idpAdaptive(light: ShadcnHex.mutedFg, dark: ShadcnHex.mutedFgDark) static let idpBorder = Color.idpAdaptive(light: ShadcnHex.border, dark: ShadcnHex.borderDark) static let idpCard = Color.idpAdaptive(light: ShadcnHex.card, dark: ShadcnHex.cardDark) static let idpPrimary = Color.idpAdaptive(light: ShadcnHex.primary, dark: ShadcnHex.primaryDark) static let idpPrimaryForeground = Color.idpAdaptive(light: ShadcnHex.primaryFg, dark: ShadcnHex.primaryFgDark) static let idpAccentSoft = Color.idpAdaptive(light: ShadcnHex.accentSoft, dark: ShadcnHex.accentSoftDark) static let idpDestructive = Color.idpAdaptive(light: ShadcnHex.destructive, dark: ShadcnHex.destructiveDark) static let idpOK = ShadcnHex.ok static let idpWarn = ShadcnHex.warn static var idpGroupedBackground: Color { idpBackground } static var idpSecondaryGroupedBackground: Color { idpCard } static var idpTertiaryFill: Color { idpMuted } static var idpSeparator: Color { idpBorder } } extension View { func idpScreenPadding(compact: Bool) -> some View { padding(.horizontal, IdP.horizontalPadding(compact: compact)) .padding(.vertical, IdP.verticalPadding(compact: compact)) } @ViewBuilder func idpInlineNavigationTitle() -> some View { #if os(macOS) self #else navigationBarTitleDisplayMode(.inline) #endif } @ViewBuilder func idpTabBarChrome() -> some View { #if os(macOS) self #else toolbarBackground(.visible, for: .tabBar) .toolbarBackground(.regularMaterial, for: .tabBar) #endif } @ViewBuilder func idpSearchable(text: Binding, isPresented: Binding) -> some View { #if os(macOS) searchable(text: text, isPresented: isPresented) #else searchable(text: text, isPresented: isPresented, placement: .navigationBarDrawer(displayMode: .automatic)) #endif } } extension ToolbarItemPlacement { static var idpTrailingToolbar: ToolbarItemPlacement { #if os(macOS) .primaryAction #else .topBarTrailing #endif } }