Files
ShhShell/ShhShell/Themes/Theme.swift
neon443 834fb266e9 theme picker ui is pretty now
added a bunch of builtin htemes
applytheme takes a theme
added builtin themes enum
added decodeloacltheme func
added builtinthemes array property
selectedtheme is now an actual theme instead of an index
its really annoying it doesnt really work rn
2025-06-29 09:59:53 +01:00

215 lines
5.9 KiB
Swift

//
// Theme.swift
// ShhShell
//
// Created by neon443 on 27/06/2025.
//
import Foundation
import SwiftTerm
import SwiftUI
struct Theme: Hashable, Equatable, Identifiable {
var id: String = UUID().uuidString
var name: String
var ansi: [SwiftTerm.Color]
var foreground: SwiftTerm.Color
var background: SwiftTerm.Color
var cursor: SwiftTerm.Color
var cursorText: SwiftTerm.Color
var bold: SwiftTerm.Color
var selectedText: SwiftTerm.Color
var selection: SwiftTerm.Color
var themeCodable: ThemeCodable {
return ThemeCodable(
name: name,
ansi0: ansi[0].colorCodable,
ansi1: ansi[1].colorCodable,
ansi2: ansi[2].colorCodable,
ansi3: ansi[3].colorCodable,
ansi4: ansi[4].colorCodable,
ansi5: ansi[5].colorCodable,
ansi6: ansi[6].colorCodable,
ansi7: ansi[7].colorCodable,
ansi8: ansi[8].colorCodable,
ansi9: ansi[9].colorCodable,
ansi10: ansi[10].colorCodable,
ansi11: ansi[11].colorCodable,
ansi12: ansi[12].colorCodable,
ansi13: ansi[13].colorCodable,
ansi14: ansi[14].colorCodable,
ansi15: ansi[15].colorCodable,
foreground: foreground.colorCodable,
background: background.colorCodable,
cursor: cursor.colorCodable,
cursorText: cursorText.colorCodable,
bold: bold.colorCodable,
selectedText: selectedText.colorCodable,
selection: selection.colorCodable
)
}
static func decodeTheme(name: String, data: Data?) -> Theme? {
guard let data else { fatalError() }
let plistDecoder = PropertyListDecoder()
let jsonDecoder = JSONDecoder()
guard let decoded =
(try? plistDecoder.decode(ThemeCodable.self, from: data)) ??
(try? jsonDecoder.decode(ThemeCodable.self, from: data))
else { fatalError() }
var theme = Theme(
name: decoded.name ?? name,
ansi: decoded.ansi,
foreground: Color(decoded.foreground),
background: Color(decoded.background),
cursor: Color(decoded.cursor),
cursorText: Color(decoded.cursorText),
bold: Color(decoded.bold),
selectedText: Color(decoded.selectedText),
selection: Color(decoded.selection)
)
return theme
}
static func decodeLocalTheme(fileName: String) -> Theme? {
guard let path = Bundle.main.url(forResource: fileName, withExtension: "plist") else { return nil }
let themeName = path.lastPathComponent.replacingOccurrences(of: ".plist", with: "")
guard let fileContents = try? Data(contentsOf: path) else { return nil }
guard var theme = Theme.decodeTheme(name: themeName, data: fileContents) else { return nil }
theme.name = themeName
theme.id = themeName
return theme
}
static var defaultTheme: Theme {
return decodeLocalTheme(fileName: "defaultTheme")!
}
static var builtinThemes: [Theme] {
return ThemesBuiltin.allCases.map({ decodeLocalTheme(fileName: $0.rawValue)! })
}
}
enum ThemesBuiltin: String, CaseIterable, Hashable, Equatable {
case defaultTheme = "defaultTheme"
case xcodedark = "xcodedark"
case xcodedarkhc = "xcodedarkhc"
case xcodewwdc = "xcodewwdc"
case tomorrowNight = "tomorrowNight"
case zeroXNineSixF = "0x96f"
case iTerm2SolarizedDark = "iTerm2SolarizedDark"
case iTerm2SolarizedLight = "iTerm2SolarizedLight"
case catppuccinFrappe = "catppuccinFrappe"
case catppuccinMocha = "catppuccinMocha"
case dracula = "dracula"
case gruvboxDark = "gruvboxDark"
case ubuntu = "ubuntu"
}
struct ThemeCodable: Codable {
var name: String?
var ansi0: ColorCodable
var ansi1: ColorCodable
var ansi2: ColorCodable
var ansi3: ColorCodable
var ansi4: ColorCodable
var ansi5: ColorCodable
var ansi6: ColorCodable
var ansi7: ColorCodable
var ansi8: ColorCodable
var ansi9: ColorCodable
var ansi10: ColorCodable
var ansi11: ColorCodable
var ansi12: ColorCodable
var ansi13: ColorCodable
var ansi14: ColorCodable
var ansi15: ColorCodable
var foreground: ColorCodable
var background: ColorCodable
var cursor: ColorCodable
var cursorText: ColorCodable
var bold: ColorCodable
var selectedText: ColorCodable
var selection: ColorCodable
enum CodingKeys: String, CodingKey {
case ansi0 = "Ansi 0 Color"
case ansi1 = "Ansi 1 Color"
case ansi2 = "Ansi 2 Color"
case ansi3 = "Ansi 3 Color"
case ansi4 = "Ansi 4 Color"
case ansi5 = "Ansi 5 Color"
case ansi6 = "Ansi 6 Color"
case ansi7 = "Ansi 7 Color"
case ansi8 = "Ansi 8 Color"
case ansi9 = "Ansi 9 Color"
case ansi10 = "Ansi 10 Color"
case ansi11 = "Ansi 11 Color"
case ansi12 = "Ansi 12 Color"
case ansi13 = "Ansi 13 Color"
case ansi14 = "Ansi 14 Color"
case ansi15 = "Ansi 15 Color"
case foreground = "Foreground Color"
case background = "Background Color"
case cursor = "Cursor Color"
case cursorText = "Cursor Text Color"
case bold = "Bold Color"
case selectedText = "Selected Text Color"
case selection = "Selection Color"
}
}
extension ThemeCodable {
var ansi: [SwiftTerm.Color] {
let arr = [ansi0, ansi1, ansi2, ansi3, ansi4, ansi5, ansi6, ansi7, ansi8, ansi9, ansi10, ansi11, ansi12, ansi13, ansi14, ansi15]
return arr.map(SwiftTerm.Color.init)
}
}
struct ColorCodable: Codable {
var red: Double
var green: Double
var blue: Double
enum CodingKeys: String, CodingKey {
case red = "Red Component"
case green = "Green Component"
case blue = "Blue Component"
}
}
extension SwiftTerm.Color {
convenience init(_ colorCodable: ColorCodable) {
let red = UInt16(colorCodable.red * 65535)
let green = UInt16(colorCodable.green * 65535)
let blue = UInt16(colorCodable.blue * 65535)
self.init(red: red, green: green, blue: blue)
}
var colorCodable: ColorCodable {
let red = Double(self.red)/65535
let green = Double(self.green)/65535
let blue = Double(self.blue)/65535
return ColorCodable(red: red, green: green, blue: blue)
}
}
extension SwiftTerm.Color {
var suiColor: SwiftUI.Color {
return Color(uiColor: self.uiColor)
}
var uiColor: UIColor {
let red = CGFloat(self.red)/65535
let green = CGFloat(self.green)/65535
let blue = CGFloat(self.blue)/65535
return UIColor(red: red, green: green, blue: blue, alpha: 1)
}
}