Files

101 lines
3.8 KiB
Swift

// SPDX-FileCopyrightText: 2026 Kaede Akino
// SPDX-License-Identifier: GPL-3.0-only
import GhosttyTheme
import SwiftUI
import TermindDomain
/// Read-only presentation of the existing light/dark preferences. Opening a browser never repairs storage.
public struct TerminalThemeOptions {
public let preferences: TerminalPreferences
public let isDark: Bool
public let library: TerminalThemeLibrary
public init(preferences: TerminalPreferences, isDark: Bool, library: TerminalThemeLibrary = .init()) {
self.preferences = preferences
self.isDark = isDark
self.library = library
}
public var selectedCustomTheme: TerminalCustomTheme? { library.selectedTheme(isDark: isDark) }
private var selection: TerminalThemeSelection? { library.selection(isDark: isDark) }
public var name: String {
selectedCustomTheme?.baseThemeName ?? selection?.builtInThemeName
?? (isDark ? preferences.darkTheme : preferences.lightTheme)
}
public var isCustom: Bool {
if selection != nil { return selectedCustomTheme != nil }
return isDark ? preferences.darkCustomEnabled : preferences.lightCustomEnabled
}
public var customText: String {
if selection != nil { return selectedCustomTheme?.configuration ?? "" }
return isDark ? preferences.darkCustomConfiguration : preferences.lightCustomConfiguration
}
public var title: LocalizedStringResource {
isDark ? AppStrings.Settings.darkTheme : AppStrings.Settings.lightTheme
}
public var summary: Text {
if let theme = selectedCustomTheme {
Text(verbatim: theme.name)
} else if isCustom {
Text(AppStrings.Settings.customTheme)
} else if name.isEmpty {
Text(AppStrings.Settings.noThemeSelected)
} else {
Text(verbatim: name)
}
}
public var isAvailable: Bool {
if let theme = selectedCustomTheme {
return TermindTerminalThemeCatalog.theme(named: theme.baseThemeName) != nil
}
if selection?.customThemeID != nil { return false }
return TermindTerminalThemeCatalog.theme(named: name)?.isDark == isDark
}
public var baseTheme: GhosttyThemeDefinition {
selectedCustomTheme?.baseTheme ?? TermindTerminalThemeCatalog.resolvedTheme(named: name, isDark: isDark)
}
public func themes(matching query: String) -> [GhosttyThemeDefinition] {
let query = query.trimmingCharacters(in: .whitespacesAndNewlines)
return TermindTerminalThemeCatalog.themes(isDark: isDark).filter {
query.isEmpty || $0.name.localizedStandardContains(query)
}
}
public func isSelected(_ name: String) -> Bool { !isCustom && self.name == name }
public func customThemes(matching query: String) -> [TerminalCustomTheme] {
library.themes(matching: query).filter { $0.appearance.includes(isDark: isDark) }
}
}
extension TerminalCustomTheme {
public var baseTheme: GhosttyThemeDefinition {
TermindTerminalThemeCatalog.theme(named: baseThemeName)
?? TermindTerminalThemeCatalog.fallbackTheme(isDark: appearance != .light)
}
}
extension TerminalThemeAppearance {
public var title: LocalizedStringResource {
switch self {
case .light: AppStrings.Settings.themeLight
case .dark: AppStrings.Settings.themeDark
case .both: AppStrings.Settings.themeBoth
}
}
}
public struct TerminalThemeAvailabilityNotice: View {
private let options: TerminalThemeOptions
public init(options: TerminalThemeOptions) { self.options = options }
public var body: some View {
if !options.isAvailable {
Text(AppStrings.Settings.unavailableThemeFallback(options.baseTheme.name))
.font(.caption)
.foregroundStyle(.secondary)
}
}
}