pi-remote-ios/Sources/UI/Terminal/TerminalTheme.swift

170 lines
5.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 00 and 25565535.
// 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 015) representing
/// the standard ANSI palette: 07 normal, 815 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 07 are the standard colors, 815 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 (065535). Multiplying each 8-bit
/// component by 257 (0x101) gives an exact linear mapping: 00, 25565535.
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),
]
)
}