// // Category.swift // Todos // // Created by Sam Jaffe on 3/2/26. // import Foundation import SwiftUI // https://gist.github.com/peterfriese/bb2fc5df202f6a15cc807bd87ff15193 // Inspired by https://cocoacasts.com/from-hex-to-uicolor-and-back-in-swift // Make Color codable. This includes support for transparency. // See https://www.digitalocean.com/community/tutorials/css-hex-code-colors-alpha-values extension Color: @retroactive Codable { #if os(macOS) fileprivate typealias UnifiedColor = NSColor #endif #if os(iOS) fileprivate typealias UnifiedColor = UIColor #endif init(hex: String) { var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines) hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "") var rgb: UInt64 = 0 var red: CGFloat = 0.0 var green: CGFloat = 0.0 var blue: CGFloat = 0.0 var opacity: CGFloat = 1.0 let length = hexSanitized.count Scanner(string: hexSanitized).scanHexInt64(&rgb) if length == 6 { red = CGFloat((rgb & 0xFF0000) >> 16) / 255.0 green = CGFloat((rgb & 0x00FF00) >> 8) / 255.0 blue = CGFloat(rgb & 0x0000FF) / 255.0 } else if length == 8 { red = CGFloat((rgb & 0xFF000000) >> 24) / 255.0 green = CGFloat((rgb & 0x00FF0000) >> 16) / 255.0 blue = CGFloat((rgb & 0x0000FF00) >> 8) / 255.0 opacity = CGFloat(rgb & 0x000000FF) / 255.0 } self.init(.sRGB, red: Double(red), green: Double(green), blue: Double(blue), opacity: Double(opacity)) } public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let hex = try container.decode(String.self) self.init(hex: hex) } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(hex) } var hex: String? { return toHex() } func toHex(alpha: Bool = false) -> String? { guard let components = UnifiedColor(self).cgColor.components, components.count >= 3 else { return nil } let red = Float(components[0]) let green = Float(components[1]) let blue = Float(components[2]) var opacity = Float(1.0) if components.count >= 4 { opacity = Float(components[3]) } if alpha { return String(format: "%02lX%02lX%02lX%02lX", lroundf(red * 255), lroundf(green * 255), lroundf(blue * 255), lroundf(opacity * 255)) } else { return String(format: "%02lX%02lX%02lX", lroundf(red * 255), lroundf(green * 255), lroundf(blue * 255)) } } } final class Category: Identifiable, Codable { var name: String = "" var color: Color = Color(.gray) var id: String { name } init() {} init(name: String, color: Color) { self.name = name self.color = color } var valid: Bool { !name.isEmpty } }