Merge pull request #70 from phranck/refactor/palette-consolidation

Refactor: Consolidate 6 palette structs into PalettePreset enum + SystemPalette
This commit is contained in:
phranck
2026-02-03 22:33:44 +01:00
12 changed files with 290 additions and 562 deletions
@@ -55,7 +55,7 @@ extension View {
///
/// ```swift
/// ContentView()
/// .palette(GreenPalette())
/// .palette(SystemPalette(.green))
/// ```
///
/// - Parameter palette: The palette to apply.
@@ -1,91 +0,0 @@
// 🖥 TUIKit Terminal UI Kit for Swift
// AmberPalette.swift
//
// Created by LAYERED.work
// CC BY-NC-SA 4.0
/// Classic amber terminal palette (P3 phosphor).
///
/// Inspired by terminals like the IBM 3278 and Wyse 50.
/// All colors are generated algorithmically from a single base hue (40°)
/// using HSL transformations.
public struct AmberPalette: BlockPalette {
public let id = "amber"
public let name = "Amber"
/// The base hue used to generate all palette colors.
private static let baseHue: Double = 40
// Background
public let background: Color
// Amber text hierarchy
public let foreground: Color
public let foregroundSecondary: Color
public let foregroundTertiary: Color
// Accent
public let accent: Color
// Semantic colors
public let success: Color
public let warning: Color
public let error: Color
public let info: Color
// UI elements
public let border: Color
// Additional backgrounds
public let statusBarBackground: Color
public let appHeaderBackground: Color
public let overlayBackground: Color
public init() {
let hue = Self.baseHue
// Background: very dark, subtly tinted
self.background = Color.hsl(hue, 30, 3)
// Foregrounds: bright, saturated text
self.foreground = Color.hsl(hue, 100, 50)
self.foregroundSecondary = Color.hsl(hue, 100, 40)
self.foregroundTertiary = Color.hsl(hue, 100, 28)
// Accent: lighter/brighter variant
self.accent = Color.hsl(hue + 5, 100, 60)
// Semantic: hue-shifted from base
self.success = Color.hsl(Self.wrapHue(hue + 40), 100, 60)
self.warning = Color.hsl(Self.wrapHue(hue + 20), 100, 70)
self.error = Color.hsl(Self.wrapHue(hue - 25), 100, 60)
self.info = Color.hsl(Self.wrapHue(hue + 10), 100, 70)
// UI elements
self.border = Color.hsl(hue, 33, 26)
// Additional backgrounds
self.statusBarBackground = Color.hsl(hue, 35, 10)
self.appHeaderBackground = Color.hsl(hue, 35, 7)
self.overlayBackground = Color.hsl(hue, 30, 3)
}
}
// MARK: - Private Helpers
private extension AmberPalette {
/// Wraps a hue value to the 0360 range.
static func wrapHue(_ hue: Double) -> Double {
var wrapped = hue.truncatingRemainder(dividingBy: 360)
if wrapped < 0 { wrapped += 360 }
return wrapped
}
}
// MARK: - Convenience Accessors
extension BlockPalette where Self == AmberPalette {
/// Amber terminal palette.
public static var amber: AmberPalette { AmberPalette() }
}
@@ -1,92 +0,0 @@
// 🖥 TUIKit Terminal UI Kit for Swift
// BluePalette.swift
//
// Created by LAYERED.work
// CC BY-NC-SA 4.0
/// Blue VFD terminal palette.
///
/// Inspired by vintage vacuum fluorescent displays (VFDs) found in
/// audio equipment, cash registers, and instrument panels.
/// All colors are generated algorithmically from a single base hue (200°)
/// using HSL transformations.
public struct BluePalette: BlockPalette {
public let id = "blue"
public let name = "Blue"
/// The base hue used to generate all palette colors.
private static let baseHue: Double = 200
// Background
public let background: Color
// Blue text hierarchy
public let foreground: Color
public let foregroundSecondary: Color
public let foregroundTertiary: Color
// Accent
public let accent: Color
// Semantic colors
public let success: Color
public let warning: Color
public let error: Color
public let info: Color
// UI elements
public let border: Color
// Additional backgrounds
public let statusBarBackground: Color
public let appHeaderBackground: Color
public let overlayBackground: Color
public init() {
let hue = Self.baseHue
// Background: very dark, subtly tinted
self.background = Color.hsl(hue, 30, 3)
// Foregrounds: bright, saturated text
self.foreground = Color.hsl(hue, 100, 50)
self.foregroundSecondary = Color.hsl(hue, 100, 40)
self.foregroundTertiary = Color.hsl(hue, 100, 30)
// Accent: lighter/brighter variant
self.accent = Color.hsl(hue, 100, 60)
// Semantic: hue-shifted from base
self.success = Color.hsl(Self.wrapHue(hue + 10), 100, 60)
self.warning = Color.hsl(Self.wrapHue(hue + 20), 100, 70)
self.error = Color.hsl(Self.wrapHue(hue - 185), 100, 60)
self.info = Color.hsl(Self.wrapHue(hue + 5), 100, 75)
// UI elements
self.border = Color.hsl(hue, 33, 26)
// Additional backgrounds
self.statusBarBackground = Color.hsl(hue, 35, 10)
self.appHeaderBackground = Color.hsl(hue, 35, 7)
self.overlayBackground = Color.hsl(hue, 30, 3)
}
}
// MARK: - Private Helpers
private extension BluePalette {
/// Wraps a hue value to the 0360 range.
static func wrapHue(_ hue: Double) -> Double {
var wrapped = hue.truncatingRemainder(dividingBy: 360)
if wrapped < 0 { wrapped += 360 }
return wrapped
}
}
// MARK: - Convenience Accessors
extension BlockPalette where Self == BluePalette {
/// Blue VFD terminal palette.
public static var blue: BluePalette { BluePalette() }
}
@@ -1,93 +0,0 @@
// 🖥 TUIKit Terminal UI Kit for Swift
// GreenPalette.swift
//
// Created by LAYERED.work
// CC BY-NC-SA 4.0
/// Classic green terminal palette (P1 phosphor).
///
/// Inspired by early CRT monitors like the IBM 5151 and Apple II.
/// All colors are generated algorithmically from a single base hue (120°)
/// using HSL transformations.
public struct GreenPalette: BlockPalette {
public let id = "green"
public let name = "Green"
/// The base hue used to generate all palette colors.
private static let baseHue: Double = 120
// Background
public let background: Color
// Green text hierarchy
public let foreground: Color
public let foregroundSecondary: Color
public let foregroundTertiary: Color
// Accent
public let accent: Color
// Semantic colors
public let success: Color
public let warning: Color
public let error: Color
public let info: Color
// UI elements
public let border: Color
// Additional backgrounds
public let statusBarBackground: Color
public let appHeaderBackground: Color
public let overlayBackground: Color
public init() {
let hue = Self.baseHue
// Background: very dark, subtly tinted
self.background = Color.hsl(hue, 30, 3)
// Foregrounds: bright, saturated text
self.foreground = Color.hsl(hue, 100, 60)
self.foregroundSecondary = Color.hsl(hue, 67, 46)
self.foregroundTertiary = Color.hsl(hue, 64, 34)
// Accent: lighter/brighter variant
self.accent = Color.hsl(hue, 100, 70)
// Semantic: hue-shifted from base
self.success = Color.hsl(hue, 100, 60)
self.warning = Color.hsl(Self.wrapHue(hue - 45), 100, 60)
self.error = Color.hsl(Self.wrapHue(hue - 105), 100, 60)
self.info = Color.hsl(Self.wrapHue(hue + 45), 100, 60)
// UI elements
self.border = Color.hsl(hue, 33, 26)
// Additional backgrounds
self.statusBarBackground = Color.hsl(hue, 35, 10)
self.appHeaderBackground = Color.hsl(hue, 35, 7)
self.overlayBackground = Color.hsl(hue, 30, 3)
}
}
// MARK: - Private Helpers
private extension GreenPalette {
/// Wraps a hue value to the 0360 range.
static func wrapHue(_ hue: Double) -> Double {
var wrapped = hue.truncatingRemainder(dividingBy: 360)
if wrapped < 0 { wrapped += 360 }
return wrapped
}
}
// MARK: - Convenience Accessors
extension BlockPalette where Self == GreenPalette {
/// The default palette (green).
public static var `default`: GreenPalette { GreenPalette() }
/// Green terminal palette.
public static var green: GreenPalette { GreenPalette() }
}
@@ -0,0 +1,276 @@
// 🖥 TUIKit Terminal UI Kit for Swift
// PalettePreset.swift
//
// Created by LAYERED.work
// CC BY-NC-SA 4.0
// MARK: - System Palette
/// A palette generated from a built-in ``Preset``.
///
/// All colors are derived algorithmically from the preset's base hue
/// and hand-tuned HSL parameters. This single type replaces the six
/// individual palette structs (`GreenPalette`, `AmberPalette`, etc.).
///
/// Custom palettes should conform to ``Palette`` or ``BlockPalette`` directly
/// instead of using this type.
///
/// # Usage
///
/// ```swift
/// let palette = SystemPalette(.amber)
/// paletteManager.setCurrent(SystemPalette(.green))
/// ```
public struct SystemPalette: BlockPalette {
// MARK: - Preset
/// Built-in palette presets inspired by classic terminal phosphors.
///
/// | Preset | Hue | Inspiration |
/// |----------|------|--------------------------------------------|
/// | `green` | 120° | IBM 5151, Apple II (P1 phosphor) |
/// | `amber` | 40° | IBM 3278, Wyse 50 (P3 phosphor) |
/// | `red` | 0° | Military/specialized, night-vision |
/// | `violet` | 270° | Retro computing, sci-fi terminals |
/// | `blue` | 200° | Vacuum fluorescent displays (VFDs) |
/// | `white` | 225° | DEC VT100/VT220 (P4 phosphor) |
public enum Preset: String, CaseIterable, Sendable {
case green
case amber
case red
case violet
case blue
case white
}
public let id: String
public let name: String
// Background
public let background: Color
// Foreground hierarchy
public let foreground: Color
public let foregroundSecondary: Color
public let foregroundTertiary: Color
// Accent
public let accent: Color
// Semantic colors
public let success: Color
public let warning: Color
public let error: Color
public let info: Color
// UI elements
public let border: Color
// Additional backgrounds
public let statusBarBackground: Color
public let appHeaderBackground: Color
public let overlayBackground: Color
/// Creates a palette from a preset.
///
/// - Parameter preset: The built-in preset to use.
public init(_ preset: Preset) {
let tuning = Tuning.for(preset)
let hue = tuning.baseHue
self.id = preset.rawValue
self.name = preset.rawValue.capitalized
// Backgrounds
self.background = Color.hsl(hue, tuning.bgSaturation, 3)
self.statusBarBackground = Color.hsl(hue, tuning.barSaturation, 10)
self.appHeaderBackground = Color.hsl(hue, tuning.barSaturation, 7)
self.overlayBackground = Color.hsl(hue, tuning.bgSaturation, 3)
// Foregrounds
self.foreground = Color.hsl(tuning.fgHue, tuning.fgSaturation, tuning.fgLightness)
self.foregroundSecondary = Color.hsl(tuning.fgHue, tuning.fgSecSaturation, tuning.fgSecLightness)
self.foregroundTertiary = Color.hsl(tuning.fgHue, tuning.fgTerSaturation, tuning.fgTerLightness)
// Accent
self.accent = Color.hsl(tuning.accentHue, tuning.accentSaturation, tuning.accentLightness)
// Semantic colors
self.success = Color.hsl(tuning.successHue, tuning.successSaturation, tuning.successLightness)
self.warning = Color.hsl(tuning.warningHue, tuning.warningSaturation, tuning.warningLightness)
self.error = Color.hsl(tuning.errorHue, tuning.errorSaturation, tuning.errorLightness)
self.info = Color.hsl(tuning.infoHue, tuning.infoSaturation, tuning.infoLightness)
// UI elements
self.border = Color.hsl(hue, tuning.borderSaturation, tuning.borderLightness)
}
}
// MARK: - Tuning Data
private extension SystemPalette {
/// Hand-tuned HSL parameters for each preset.
struct Tuning {
// Base
let baseHue: Double
let bgSaturation: Double
let barSaturation: Double
// Foreground
let fgHue: Double
let fgSaturation: Double
let fgLightness: Double
let fgSecSaturation: Double
let fgSecLightness: Double
let fgTerSaturation: Double
let fgTerLightness: Double
// Accent
let accentHue: Double
let accentSaturation: Double
let accentLightness: Double
// Semantic
let successHue: Double
let successSaturation: Double
let successLightness: Double
let warningHue: Double
let warningSaturation: Double
let warningLightness: Double
let errorHue: Double
let errorSaturation: Double
let errorLightness: Double
let infoHue: Double
let infoSaturation: Double
let infoLightness: Double
// Border
let borderSaturation: Double
let borderLightness: Double
}
}
// MARK: - Preset Tuning Values
private extension SystemPalette.Tuning {
/// Returns the tuning parameters for a given preset.
static func `for`(_ preset: SystemPalette.Preset) -> SystemPalette.Tuning {
switch preset {
case .green:
SystemPalette.Tuning(
baseHue: 120, bgSaturation: 30, barSaturation: 35,
fgHue: 120, fgSaturation: 100, fgLightness: 60,
fgSecSaturation: 67, fgSecLightness: 46,
fgTerSaturation: 64, fgTerLightness: 34,
accentHue: 120, accentSaturation: 100, accentLightness: 70,
successHue: 120, successSaturation: 100, successLightness: 60,
warningHue: wrapHue(120 - 45), warningSaturation: 100, warningLightness: 60,
errorHue: wrapHue(120 - 105), errorSaturation: 100, errorLightness: 60,
infoHue: wrapHue(120 + 45), infoSaturation: 100, infoLightness: 60,
borderSaturation: 33, borderLightness: 26
)
case .amber:
SystemPalette.Tuning(
baseHue: 40, bgSaturation: 30, barSaturation: 35,
fgHue: 40, fgSaturation: 100, fgLightness: 50,
fgSecSaturation: 100, fgSecLightness: 40,
fgTerSaturation: 100, fgTerLightness: 28,
accentHue: 45, accentSaturation: 100, accentLightness: 60,
successHue: wrapHue(40 + 40), successSaturation: 100, successLightness: 60,
warningHue: wrapHue(40 + 20), warningSaturation: 100, warningLightness: 70,
errorHue: wrapHue(40 - 25), errorSaturation: 100, errorLightness: 60,
infoHue: wrapHue(40 + 10), infoSaturation: 100, infoLightness: 70,
borderSaturation: 33, borderLightness: 26
)
case .red:
SystemPalette.Tuning(
baseHue: 0, bgSaturation: 30, barSaturation: 35,
fgHue: 0, fgSaturation: 100, fgLightness: 63,
fgSecSaturation: 60, fgSecLightness: 50,
fgTerSaturation: 62, fgTerLightness: 35,
accentHue: 0, accentSaturation: 100, accentLightness: 70,
successHue: wrapHue(0 + 30), successSaturation: 100, successLightness: 75,
warningHue: wrapHue(0 + 30), warningSaturation: 100, warningLightness: 70,
errorHue: 0, errorSaturation: 0, errorLightness: 100,
infoHue: 0, infoSaturation: 100, infoLightness: 80,
borderSaturation: 33, borderLightness: 26
)
case .violet:
SystemPalette.Tuning(
baseHue: 270, bgSaturation: 30, barSaturation: 35,
fgHue: 270, fgSaturation: 80, fgLightness: 70,
fgSecSaturation: 70, fgSecLightness: 55,
fgTerSaturation: 60, fgTerLightness: 40,
accentHue: 270, accentSaturation: 85, accentLightness: 78,
successHue: wrapHue(270 + 120), successSaturation: 70, successLightness: 65,
warningHue: wrapHue(270 + 60), warningSaturation: 80, warningLightness: 70,
errorHue: wrapHue(270 + 180), errorSaturation: 85, errorLightness: 65,
infoHue: wrapHue(270 - 60), infoSaturation: 70, infoLightness: 70,
borderSaturation: 40, borderLightness: 25
)
case .blue:
SystemPalette.Tuning(
baseHue: 200, bgSaturation: 30, barSaturation: 35,
fgHue: 200, fgSaturation: 100, fgLightness: 50,
fgSecSaturation: 100, fgSecLightness: 40,
fgTerSaturation: 100, fgTerLightness: 30,
accentHue: 200, accentSaturation: 100, accentLightness: 60,
successHue: wrapHue(200 + 10), successSaturation: 100, successLightness: 60,
warningHue: wrapHue(200 + 20), warningSaturation: 100, warningLightness: 70,
errorHue: wrapHue(200 - 185), errorSaturation: 100, errorLightness: 60,
infoHue: wrapHue(200 + 5), infoSaturation: 100, infoLightness: 75,
borderSaturation: 33, borderLightness: 26
)
case .white:
SystemPalette.Tuning(
baseHue: 225, bgSaturation: 25, barSaturation: 20,
fgHue: 0, fgSaturation: 0, fgLightness: 91,
fgSecSaturation: 0, fgSecLightness: 69,
fgTerSaturation: 0, fgTerLightness: 47,
accentHue: 0, accentSaturation: 0, accentLightness: 100,
successHue: 120, successSaturation: 50, successLightness: 75,
warningHue: 40, warningSaturation: 60, warningLightness: 75,
errorHue: 0, errorSaturation: 60, errorLightness: 75,
infoHue: 210, infoSaturation: 60, infoLightness: 75,
borderSaturation: 0, borderLightness: 28
)
}
}
/// Wraps a hue value to the 0360 range.
private static func wrapHue(_ hue: Double) -> Double {
var wrapped = hue.truncatingRemainder(dividingBy: 360)
if wrapped < 0 { wrapped += 360 }
return wrapped
}
}
// MARK: - Convenience Accessors
extension BlockPalette where Self == SystemPalette {
/// The default palette (green).
public static var `default`: SystemPalette { SystemPalette(.green) }
/// Green terminal palette (P1 phosphor).
public static var green: SystemPalette { SystemPalette(.green) }
/// Amber terminal palette (P3 phosphor).
public static var amber: SystemPalette { SystemPalette(.amber) }
/// Red terminal palette (night-vision).
public static var red: SystemPalette { SystemPalette(.red) }
/// Violet terminal palette (retro/sci-fi).
public static var violet: SystemPalette { SystemPalette(.violet) }
/// Blue VFD terminal palette.
public static var blue: SystemPalette { SystemPalette(.blue) }
/// White terminal palette (P4 phosphor).
public static var white: SystemPalette { SystemPalette(.white) }
}
@@ -1,92 +0,0 @@
// 🖥 TUIKit Terminal UI Kit for Swift
// RedPalette.swift
//
// Created by LAYERED.work
// CC BY-NC-SA 4.0
/// Red terminal palette.
///
/// Less common but used in some military and specialized applications.
/// Night-vision friendly with reduced eye strain in dark environments.
/// All colors are generated algorithmically from a single base hue (0°)
/// using HSL transformations.
public struct RedPalette: BlockPalette {
public let id = "red"
public let name = "Red"
/// The base hue used to generate all palette colors.
private static let baseHue: Double = 0
// Background
public let background: Color
// Red text hierarchy
public let foreground: Color
public let foregroundSecondary: Color
public let foregroundTertiary: Color
// Accent
public let accent: Color
// Semantic colors
public let success: Color
public let warning: Color
public let error: Color
public let info: Color
// UI elements
public let border: Color
// Additional backgrounds
public let statusBarBackground: Color
public let appHeaderBackground: Color
public let overlayBackground: Color
public init() {
let hue = Self.baseHue
// Background: very dark, subtly tinted
self.background = Color.hsl(hue, 30, 3)
// Foregrounds: bright, saturated text
self.foreground = Color.hsl(hue, 100, 63)
self.foregroundSecondary = Color.hsl(hue, 60, 50)
self.foregroundTertiary = Color.hsl(hue, 62, 35)
// Accent: lighter/brighter variant
self.accent = Color.hsl(hue, 100, 70)
// Semantic: hue-shifted from base
self.success = Color.hsl(Self.wrapHue(hue + 30), 100, 75)
self.warning = Color.hsl(Self.wrapHue(hue + 30), 100, 70)
self.error = Color.hsl(0, 0, 100)
self.info = Color.hsl(hue, 100, 80)
// UI elements
self.border = Color.hsl(hue, 33, 26)
// Additional backgrounds
self.statusBarBackground = Color.hsl(hue, 35, 10)
self.appHeaderBackground = Color.hsl(hue, 35, 7)
self.overlayBackground = Color.hsl(hue, 30, 3)
}
}
// MARK: - Private Helpers
private extension RedPalette {
/// Wraps a hue value to the 0360 range.
static func wrapHue(_ hue: Double) -> Double {
var wrapped = hue.truncatingRemainder(dividingBy: 360)
if wrapped < 0 { wrapped += 360 }
return wrapped
}
}
// MARK: - Convenience Accessors
extension BlockPalette where Self == RedPalette {
/// Red terminal palette.
public static var red: RedPalette { RedPalette() }
}
@@ -1,91 +0,0 @@
// 🖥 TUIKit Terminal UI Kit for Swift
// VioletPalette.swift
//
// Created by LAYERED.work
// CC BY-NC-SA 4.0
/// Violet terminal palette.
///
/// Inspired by retro computing aesthetics and sci-fi terminal displays.
/// All colors are generated algorithmically from a single base hue (270°)
/// using HSL transformations.
public struct VioletPalette: BlockPalette {
public let id = "violet"
public let name = "Violet"
/// The base hue used to generate all palette colors.
private static let baseHue: Double = 270
// Background
public let background: Color
// Violet text hierarchy
public let foreground: Color
public let foregroundSecondary: Color
public let foregroundTertiary: Color
// Accent
public let accent: Color
// Semantic colors
public let success: Color
public let warning: Color
public let error: Color
public let info: Color
// UI elements
public let border: Color
// Additional backgrounds
public let statusBarBackground: Color
public let appHeaderBackground: Color
public let overlayBackground: Color
public init() {
let hue = Self.baseHue
// Background: very dark, subtly tinted
self.background = Color.hsl(hue, 30, 3)
// Foregrounds: bright, saturated text
self.foreground = Color.hsl(hue, 80, 70)
self.foregroundSecondary = Color.hsl(hue, 70, 55)
self.foregroundTertiary = Color.hsl(hue, 60, 40)
// Accent: lighter/brighter variant
self.accent = Color.hsl(hue, 85, 78)
// Semantic: hue-shifted from base
self.success = Color.hsl(Self.wrapHue(hue + 120), 70, 65)
self.warning = Color.hsl(Self.wrapHue(hue + 60), 80, 70)
self.error = Color.hsl(Self.wrapHue(hue + 180), 85, 65)
self.info = Color.hsl(Self.wrapHue(hue - 60), 70, 70)
// UI elements
self.border = Color.hsl(hue, 40, 25)
// Additional backgrounds
self.statusBarBackground = Color.hsl(hue, 35, 10)
self.appHeaderBackground = Color.hsl(hue, 35, 7)
self.overlayBackground = Color.hsl(hue, 30, 3)
}
}
// MARK: - Private Helpers
private extension VioletPalette {
/// Wraps a hue value to the 0360 range.
static func wrapHue(_ hue: Double) -> Double {
var wrapped = hue.truncatingRemainder(dividingBy: 360)
if wrapped < 0 { wrapped += 360 }
return wrapped
}
}
// MARK: - Convenience Accessors
extension BlockPalette where Self == VioletPalette {
/// Violet terminal palette.
public static var violet: VioletPalette { VioletPalette() }
}
@@ -1,80 +0,0 @@
// 🖥 TUIKit Terminal UI Kit for Swift
// WhitePalette.swift
//
// Created by LAYERED.work
// CC BY-NC-SA 4.0
/// Classic white terminal palette (P4 phosphor).
///
/// Inspired by terminals like the DEC VT100 and VT220.
/// Near-achromatic palette with a subtle cool blue tint (225°) in
/// backgrounds. Foregrounds are neutral gray. All colors are generated
/// algorithmically using HSL transformations.
public struct WhitePalette: BlockPalette {
public let id = "white"
public let name = "White"
/// The base hue used for the subtle cool tint in backgrounds.
private static let baseHue: Double = 225
// Background
public let background: Color
// White/gray text hierarchy
public let foreground: Color
public let foregroundSecondary: Color
public let foregroundTertiary: Color
// Accent
public let accent: Color
// Semantic colors
public let success: Color
public let warning: Color
public let error: Color
public let info: Color
// UI elements
public let border: Color
// Additional backgrounds
public let statusBarBackground: Color
public let appHeaderBackground: Color
public let overlayBackground: Color
public init() {
let hue = Self.baseHue
// Background: very dark, subtle cool tint
self.background = Color.hsl(hue, 25, 3)
// Foregrounds: near-neutral gray (very low saturation)
self.foreground = Color.hsl(0, 0, 91)
self.foregroundSecondary = Color.hsl(0, 0, 69)
self.foregroundTertiary = Color.hsl(0, 0, 47)
// Accent: pure white
self.accent = Color.hsl(0, 0, 100)
// Semantic colors: subtle tints on neutral base
self.success = Color.hsl(120, 50, 75)
self.warning = Color.hsl(40, 60, 75)
self.error = Color.hsl(0, 60, 75)
self.info = Color.hsl(210, 60, 75)
// UI elements: neutral gray
self.border = Color.hsl(0, 0, 28)
// Additional backgrounds: subtle cool tint
self.statusBarBackground = Color.hsl(hue, 20, 10)
self.appHeaderBackground = Color.hsl(hue, 20, 7)
self.overlayBackground = Color.hsl(hue, 25, 3)
}
}
// MARK: - Convenience Accessors
extension BlockPalette where Self == WhitePalette {
/// White terminal palette.
public static var white: WhitePalette { WhitePalette() }
}
+6 -13
View File
@@ -23,7 +23,7 @@ import Foundation
///
/// ```swift
/// // Set the app palette
/// paletteManager.setCurrent(AmberPalette())
/// paletteManager.setCurrent(SystemPalette(.amber))
///
/// // Use palette colors in views
/// Text("Hello").foregroundColor(.palette.foreground)
@@ -165,7 +165,7 @@ extension Palette {
/// Environment key for the current palette.
private struct PaletteKey: EnvironmentKey {
static let defaultValue: any Palette = GreenPalette()
static let defaultValue: any Palette = SystemPalette(.green)
}
extension EnvironmentValues {
@@ -177,7 +177,7 @@ extension EnvironmentValues {
/// WindowGroup {
/// ContentView()
/// }
/// .environment(\.palette, GreenPalette())
/// .environment(\.palette, SystemPalette(.green))
/// ```
///
/// Access the palette in `renderToBuffer(context:)`:
@@ -196,17 +196,10 @@ extension EnvironmentValues {
/// Registry of available palettes.
struct PaletteRegistry {
/// All available palettes in cycling order.
/// All available palettes in cycling order, built from ``SystemPalette/Preset``.
///
/// Order: Green Amber Red Violet Blue White
static let all: [any Palette] = [
GreenPalette(),
AmberPalette(),
RedPalette(),
VioletPalette(),
BluePalette(),
WhitePalette(),
]
static let all: [any Palette] = SystemPalette.Preset.allCases.map { SystemPalette($0) }
/// Finds a palette by ID.
static func palette(withId id: String) -> (any Palette)? {
@@ -232,7 +225,7 @@ extension EnvironmentValues {
/// ```swift
/// let paletteManager = context.environment.paletteManager
/// paletteManager.cycleNext()
/// paletteManager.setCurrent(AmberPalette())
/// paletteManager.setCurrent(SystemPalette(.amber))
/// ```
public var paletteManager: ThemeManager {
get { self[PaletteManagerKey.self] }
+1 -1
View File
@@ -51,7 +51,7 @@ public protocol Cyclable: Sendable {
/// // Access the palette manager via context.environment
/// let paletteManager = context.environment.paletteManager
/// paletteManager.cycleNext()
/// paletteManager.setCurrent(AmberPalette())
/// paletteManager.setCurrent(SystemPalette(.amber))
/// let name = paletteManager.currentName
/// ```
///
@@ -31,7 +31,7 @@ struct GreenPaletteTests {
@Test("Green palette block surfaces get progressively brighter")
func greenBlockSurfaceLuminanceOrder() throws {
let palette = GreenPalette()
let palette = SystemPalette(.green)
let bgLum = try #require(relativeLuminance(of: palette.background))
let headerLum = try #require(relativeLuminance(of: palette.surfaceHeaderBackground))
let surfaceLum = try #require(relativeLuminance(of: palette.surfaceBackground))
@@ -44,7 +44,7 @@ struct GreenPaletteTests {
@Test("Green palette foregrounds get progressively dimmer")
func greenForegroundLuminanceOrder() throws {
let palette = GreenPalette()
let palette = SystemPalette(.green)
let fgLum = try #require(relativeLuminance(of: palette.foreground))
let fgSecLum = try #require(relativeLuminance(of: palette.foregroundSecondary))
let fgTerLum = try #require(relativeLuminance(of: palette.foregroundTertiary))
@@ -55,8 +55,6 @@ struct GreenPaletteTests {
}
// MARK: - Violet Palette Tests
@Suite("Violet Palette Tests")
@@ -64,7 +62,7 @@ struct VioletPaletteTests {
@Test("Violet palette block surfaces get progressively brighter")
func violetBlockSurfaceLuminanceOrder() throws {
let palette = VioletPalette()
let palette = SystemPalette(.violet)
let bgLum = try #require(relativeLuminance(of: palette.background))
let headerLum = try #require(relativeLuminance(of: palette.surfaceHeaderBackground))
let surfaceLum = try #require(relativeLuminance(of: palette.surfaceBackground))
@@ -84,7 +82,7 @@ struct BluePaletteTests {
@Test("Blue palette block surfaces get progressively brighter")
func blueBlockSurfaceLuminanceOrder() throws {
let palette = BluePalette()
let palette = SystemPalette(.blue)
let bgLum = try #require(relativeLuminance(of: palette.background))
let headerLum = try #require(relativeLuminance(of: palette.surfaceHeaderBackground))
let surfaceLum = try #require(relativeLuminance(of: palette.surfaceBackground))
@@ -99,8 +99,8 @@ struct ThemeManagerCyclingTests {
@Test("ThemeManager with palette items returns currentPalette")
func paletteManagerReturnsPalette() {
let palettes: [any Cyclable] = [
GreenPalette(),
AmberPalette(),
SystemPalette(.green),
SystemPalette(.amber),
]
let manager = ThemeManager(items: palettes)
#expect(manager.currentPalette != nil)