import SwiftUI /// The colour and icon vocabulary `icon`, `iconColor`, and `background` are written in. /// /// **Mirrors `Kanban/UI/Palette.swift` and `SymbolPickerCatalog`, not shared with them.** /// `project.yml` excludes `Kanban/UI` from the phone target outright — it is AppKit-only — so this /// is an independent copy of the same name→hex tables and the same curated symbol grid, kept as /// pure data (no `NSColor`, no `AppKit`). The two pickers are free to diverge; nothing here reads /// the Mac's file or vice versa. enum CardPalette { struct Swatch: Identifiable, Sendable { /// Kebab-case, exactly as written to frontmatter (`iconColor`/`background`'s `color` /// subkey read this way — see `FrontmatterFields.swift`). let name: String let hex: String var id: String { name } } /// `iconColor`'s twelve wells. static let foregrounds: [Swatch] = [ Swatch(name: "obsidian", hex: "#000000"), Swatch(name: "aluminum", hex: "#9B9B9B"), Swatch(name: "soapstone", hex: "#D5D5D5"), Swatch(name: "chalk", hex: "#FFFFFF"), Swatch(name: "carnation", hex: "#FF576C"), Swatch(name: "rich-grapefruit", hex: "#FF864C"), Swatch(name: "smokey-tangerine", hex: "#E5A334"), Swatch(name: "fern", hex: "#50B23D"), Swatch(name: "light-teal", hex: "#00B7B7"), Swatch(name: "deep-sky-blue", hex: "#0084E5"), Swatch(name: "pale-violet", hex: "#8C59C5"), Swatch(name: "deep-cool-granite", hex: "#597199"), ] /// `background`'s twelve wells — written into the mapping's `color` subkey /// (`FrontmatterDocument.setStyleValue`). static let backgrounds: [Swatch] = [ Swatch(name: "obsidian", hex: "#000000"), Swatch(name: "shale", hex: "#5B5B5B"), Swatch(name: "aluminum", hex: "#9B9B9B"), Swatch(name: "chalk", hex: "#FFFFFF"), Swatch(name: "light-cayenne", hex: "#B6071E"), Swatch(name: "light-mocha", hex: "#B73C14"), Swatch(name: "smokey-mocha", hex: "#674611"), Swatch(name: "smokey-fern", hex: "#145312"), Swatch(name: "dark-teal", hex: "#005152"), Swatch(name: "smokey-ocean", hex: "#003168"), Swatch(name: "smokey-rich-eggplant", hex: "#290659"), Swatch(name: "intense-cool-shale", hex: "#1F2E45"), ] /// A general "boards and projects" grid — `SymbolPickerCatalog.defaultSet`, mirrored for the /// same reason as the colour tables above. static let icons: [String] = [ "star", "flag", "heart", "bolt", "flame", "leaf", "drop", "sun.max", "moon", "sparkles", "tag", "bookmark", "pin", "bell", "paperplane", "tray", "folder", "archivebox", "doc.text", "list.bullet", "checklist", "calendar", "clock", "hammer", "wrench.and.screwdriver", "paintbrush", "lightbulb", "brain", "book", "graduationcap", "briefcase", "cart", "house", "airplane", "gamecontroller", "globe", ] /// Resolves a stored palette name to a colour, for rendering a well or a row accent. /// /// **Lenient, like the field itself** (`Kanban/UI/Palette.swift`'s own framing): an /// unrecognized name — a custom hex the field allows but this picker does not offer, a typo, a /// name from some future palette — answers `nil`, and every caller here falls back to its own /// default rather than guessing at a colour. Nothing on disk is affected either way. static func color(named name: String, in swatches: [Swatch]) -> Color? { guard let swatch = swatches.first(where: { $0.name == name }) else { return nil } return Color(paletteHex: swatch.hex) } } private extension Color { /// `#RRGGBB` or `#RRGGBBAA` — the two forms `CardPalette.Swatch.hex` uses. Malformed input /// answers `nil` rather than a guessed colour, matching `CardPalette.color(named:in:)`'s own /// contract. init?(paletteHex hex: String) { var value = hex if value.hasPrefix("#") { value.removeFirst() } guard value.count == 6 || value.count == 8, let intValue = UInt64(value, radix: 16) else { return nil } let r, g, b, a: Double if value.count == 8 { r = Double((intValue >> 24) & 0xFF) / 255 g = Double((intValue >> 16) & 0xFF) / 255 b = Double((intValue >> 8) & 0xFF) / 255 a = Double(intValue & 0xFF) / 255 } else { r = Double((intValue >> 16) & 0xFF) / 255 g = Double((intValue >> 8) & 0xFF) / 255 b = Double(intValue & 0xFF) / 255 a = 1 } self.init(red: r, green: g, blue: b, opacity: a) } }