170 lines
5.9 KiB
Swift
170 lines
5.9 KiB
Swift
// TerminalTheme.swift
|
||
// Defines color themes for the terminal view.
|
||
//
|
||
// SwiftTerm API notes (discovered during T-2.3):
|
||
// • SwiftTerm.Color uses UInt16 components (0...65535).
|
||
// Conversion from 8-bit: multiply by 257 (= 0x101), which maps 0→0 and 255→65535.
|
||
// • installColors([SwiftTerm.Color]) installs the 16-color ANSI palette.
|
||
// • nativeForegroundColor / nativeBackgroundColor accept UIColor.
|
||
|
||
import Foundation
|
||
import SwiftTerm
|
||
|
||
// MARK: - ThemeColor
|
||
|
||
/// A simple 8-bit RGB color that is Codable and Sendable.
|
||
public struct ThemeColor: Codable, Sendable, Equatable {
|
||
public let r: UInt8
|
||
public let g: UInt8
|
||
public let b: UInt8
|
||
|
||
public init(r: UInt8, g: UInt8, b: UInt8) {
|
||
self.r = r; self.g = g; self.b = b
|
||
}
|
||
}
|
||
|
||
// MARK: - TerminalTheme
|
||
|
||
/// A complete color theme for the terminal view.
|
||
///
|
||
/// `ansiColors` must contain exactly 16 entries (indices 0–15) representing
|
||
/// the standard ANSI palette: 0–7 normal, 8–15 bright.
|
||
public struct TerminalTheme: Codable, Identifiable, Sendable, Equatable {
|
||
public let id: String
|
||
public let name: String
|
||
public let foreground: ThemeColor
|
||
public let background: ThemeColor
|
||
public let cursor: ThemeColor
|
||
/// 16 ANSI colors: indices 0–7 are the standard colors, 8–15 are the bright variants.
|
||
public let ansiColors: [ThemeColor]
|
||
|
||
public init(
|
||
id: String,
|
||
name: String,
|
||
foreground: ThemeColor,
|
||
background: ThemeColor,
|
||
cursor: ThemeColor,
|
||
ansiColors: [ThemeColor]
|
||
) {
|
||
precondition(ansiColors.count == 16, "ansiColors must contain exactly 16 entries")
|
||
self.id = id
|
||
self.name = name
|
||
self.foreground = foreground
|
||
self.background = background
|
||
self.cursor = cursor
|
||
self.ansiColors = ansiColors
|
||
}
|
||
|
||
// MARK: SwiftTerm conversion
|
||
|
||
/// Converts a `ThemeColor` to a `SwiftTerm.Color`.
|
||
///
|
||
/// SwiftTerm.Color uses UInt16 components (0–65535). Multiplying each 8-bit
|
||
/// component by 257 (0x101) gives an exact linear mapping: 0→0, 255→65535.
|
||
public func toSwiftTermColor(_ c: ThemeColor) -> SwiftTerm.Color {
|
||
SwiftTerm.Color(
|
||
red: UInt16(c.r) * 257,
|
||
green: UInt16(c.g) * 257,
|
||
blue: UInt16(c.b) * 257
|
||
)
|
||
}
|
||
|
||
/// Returns all 16 ANSI entries as `SwiftTerm.Color` values.
|
||
public var swiftTermAnsiColors: [SwiftTerm.Color] {
|
||
ansiColors.map { toSwiftTermColor($0) }
|
||
}
|
||
}
|
||
|
||
// MARK: - Built-in themes
|
||
|
||
public extension TerminalTheme {
|
||
|
||
// MARK: dark — hacker/Matrix style: black bg, green fg
|
||
|
||
static let dark = TerminalTheme(
|
||
id: "dark",
|
||
name: "Dark (Hacker)",
|
||
foreground: ThemeColor(r: 0x33, g: 0xFF, b: 0x33), // bright green
|
||
background: ThemeColor(r: 0x00, g: 0x00, b: 0x00), // pure black
|
||
cursor: ThemeColor(r: 0x33, g: 0xFF, b: 0x33),
|
||
ansiColors: [
|
||
// 0 – black (normal)
|
||
ThemeColor(r: 0x00, g: 0x00, b: 0x00),
|
||
// 1 – red
|
||
ThemeColor(r: 0xCC, g: 0x00, b: 0x00),
|
||
// 2 – green
|
||
ThemeColor(r: 0x00, g: 0xCC, b: 0x00),
|
||
// 3 – yellow
|
||
ThemeColor(r: 0xCC, g: 0xCC, b: 0x00),
|
||
// 4 – blue
|
||
ThemeColor(r: 0x00, g: 0x00, b: 0xCC),
|
||
// 5 – magenta
|
||
ThemeColor(r: 0xCC, g: 0x00, b: 0xCC),
|
||
// 6 – cyan
|
||
ThemeColor(r: 0x00, g: 0xCC, b: 0xCC),
|
||
// 7 – white (normal)
|
||
ThemeColor(r: 0xCC, g: 0xCC, b: 0xCC),
|
||
// 8 – bright black (dark gray)
|
||
ThemeColor(r: 0x33, g: 0x33, b: 0x33),
|
||
// 9 – bright red
|
||
ThemeColor(r: 0xFF, g: 0x33, b: 0x33),
|
||
// 10 – bright green
|
||
ThemeColor(r: 0x33, g: 0xFF, b: 0x33),
|
||
// 11 – bright yellow
|
||
ThemeColor(r: 0xFF, g: 0xFF, b: 0x33),
|
||
// 12 – bright blue
|
||
ThemeColor(r: 0x33, g: 0x33, b: 0xFF),
|
||
// 13 – bright magenta
|
||
ThemeColor(r: 0xFF, g: 0x33, b: 0xFF),
|
||
// 14 – bright cyan
|
||
ThemeColor(r: 0x33, g: 0xFF, b: 0xFF),
|
||
// 15 – bright white
|
||
ThemeColor(r: 0xFF, g: 0xFF, b: 0xFF),
|
||
]
|
||
)
|
||
|
||
// MARK: github — GitHub Dark theme
|
||
|
||
static let github = TerminalTheme(
|
||
id: "github",
|
||
name: "GitHub Dark",
|
||
foreground: ThemeColor(r: 0xE6, g: 0xED, b: 0xF3), // #e6edf3
|
||
background: ThemeColor(r: 0x0D, g: 0x11, b: 0x17), // #0d1117
|
||
cursor: ThemeColor(r: 0xE6, g: 0xED, b: 0xF3),
|
||
ansiColors: [
|
||
// 0 – black
|
||
ThemeColor(r: 0x48, g: 0x4F, b: 0x58),
|
||
// 1 – red
|
||
ThemeColor(r: 0xFF, g: 0x77, b: 0x77),
|
||
// 2 – green
|
||
ThemeColor(r: 0x56, g: 0xD3, b: 0x64),
|
||
// 3 – yellow
|
||
ThemeColor(r: 0xE3, g: 0xB3, b: 0x41),
|
||
// 4 – blue
|
||
ThemeColor(r: 0x6C, g: 0xA4, b: 0xF8),
|
||
// 5 – magenta
|
||
ThemeColor(r: 0xBC, g: 0x8C, b: 0xFF),
|
||
// 6 – cyan
|
||
ThemeColor(r: 0x2B, g: 0x73, b: 0x89), // #2b7389
|
||
// 7 – white
|
||
ThemeColor(r: 0xE6, g: 0xED, b: 0xF3),
|
||
// 8 – bright black
|
||
ThemeColor(r: 0x6E, g: 0x76, b: 0x81),
|
||
// 9 – bright red
|
||
ThemeColor(r: 0xFF, g: 0xA1, b: 0x98),
|
||
// 10 – bright green
|
||
ThemeColor(r: 0x3F, g: 0xB9, b: 0x50),
|
||
// 11 – bright yellow
|
||
ThemeColor(r: 0xD2, g: 0x9C, b: 0x22),
|
||
// 12 – bright blue
|
||
ThemeColor(r: 0x79, g: 0xC0, b: 0xFF),
|
||
// 13 – bright magenta
|
||
ThemeColor(r: 0xD2, g: 0xA8, b: 0xFF),
|
||
// 14 – bright cyan
|
||
ThemeColor(r: 0x39, g: 0xC5, b: 0xCF),
|
||
// 15 – bright white
|
||
ThemeColor(r: 0xFF, g: 0xFF, b: 0xFF),
|
||
]
|
||
)
|
||
}
|