From 9d05bc586a1969842f199f3962bb81f16f0e80c6 Mon Sep 17 00:00:00 2001 From: Harvey Zuccon Date: Sun, 8 Mar 2026 15:03:42 +1100 Subject: [PATCH] Add themes selector --- .gitignore | 1 + .../CommandNotch.xcodeproj/project.pbxproj | 4 + .../UserInterfaceState.xcuserstate | Bin 33163 -> 34290 bytes Downterm/CommandNotch/AppDelegate.swift | 15 +++ .../CommandNotch/Models/NotchSettings.swift | 3 + .../CommandNotch/Models/TerminalManager.swift | 13 +- .../CommandNotch/Models/TerminalSession.swift | 18 +-- .../CommandNotch/Models/TerminalTheme.swift | 117 ++++++++++++++++++ .../CommandNotch/Views/SettingsView.swift | 16 +++ 9 files changed, 179 insertions(+), 8 deletions(-) create mode 100644 Downterm/CommandNotch/Models/TerminalTheme.swift diff --git a/.gitignore b/.gitignore index 4007749..9ffbc2f 100644 --- a/.gitignore +++ b/.gitignore @@ -80,5 +80,6 @@ dist/ build/ **/Release* +CommandNotch 20* **/.DS_Store \ No newline at end of file diff --git a/Downterm/CommandNotch.xcodeproj/project.pbxproj b/Downterm/CommandNotch.xcodeproj/project.pbxproj index c6cdacb..5e5493a 100644 --- a/Downterm/CommandNotch.xcodeproj/project.pbxproj +++ b/Downterm/CommandNotch.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 26A767A10DDA77A690CC3C37 /* NotchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 589421631401C819FE1A7BA9 /* NotchViewModel.swift */; }; 295653929D5B9C0E6C90D6D7 /* SwiftTerm in Frameworks */ = {isa = PBXBuildFile; productRef = 032AECA58EA4C274BE9F3320 /* SwiftTerm */; }; 37FC0A7CEEA37C9DCC6A8351 /* TerminalSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B598809B19C892470DE7268 /* TerminalSession.swift */; }; + 3A1F0C4BE9D84A5C8E2B7101 /* TerminalTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1F0C4AE9D84A5C8E2B7101 /* TerminalTheme.swift */; }; 4566E6B87CB62AF5C8D4B9D8 /* NotchShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FC09C538CBE7C2D072008B2 /* NotchShape.swift */; }; 4D5125E11B4DDBDB3DFACBAF /* HotkeyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B72743F178231E0B06DD3DE /* HotkeyManager.swift */; }; 5B14FC23928E817FEB8D2A74 /* NotchWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FEFF9074A85F02C43D9408 /* NotchWindow.swift */; }; @@ -50,6 +51,7 @@ 5C0779490DE9020FBBC464BE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 665CFC051CF185B71199608D /* CommandNotch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CommandNotch.app; sourceTree = BUILT_PRODUCTS_DIR; }; 7B598809B19C892470DE7268 /* TerminalSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSession.swift; sourceTree = ""; }; + 3A1F0C4AE9D84A5C8E2B7101 /* TerminalTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalTheme.swift; sourceTree = ""; }; 9547A79F60E46F4521A70674 /* CommandNotch.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = CommandNotch.entitlements; sourceTree = ""; }; AA6359CF9DDF89413440300D /* NotchSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchSettings.swift; sourceTree = ""; }; BA6843B571B41986DE386F5F /* TerminalManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalManager.swift; sourceTree = ""; }; @@ -109,6 +111,7 @@ 589421631401C819FE1A7BA9 /* NotchViewModel.swift */, BA6843B571B41986DE386F5F /* TerminalManager.swift */, 7B598809B19C892470DE7268 /* TerminalSession.swift */, + 3A1F0C4AE9D84A5C8E2B7101 /* TerminalTheme.swift */, ); path = Models; sourceTree = ""; @@ -255,6 +258,7 @@ 7EA51C3720BED7E6189E057D /* TabBar.swift in Sources */, E9A064422790735E033E534F /* TerminalManager.swift in Sources */, 37FC0A7CEEA37C9DCC6A8351 /* TerminalSession.swift in Sources */, + 3A1F0C4BE9D84A5C8E2B7101 /* TerminalTheme.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Downterm/CommandNotch.xcodeproj/project.xcworkspace/xcuserdata/harvmaster.xcuserdatad/UserInterfaceState.xcuserstate b/Downterm/CommandNotch.xcodeproj/project.xcworkspace/xcuserdata/harvmaster.xcuserdatad/UserInterfaceState.xcuserstate index ae272fab228f4b5ec4afad5c99559c5cf06d9341..17f51ba7bfec0e042d5971cd2e0ff9c78d070c5c 100644 GIT binary patch delta 12064 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 diff --git a/Downterm/CommandNotch/AppDelegate.swift b/Downterm/CommandNotch/AppDelegate.swift index 1b8a522..fd6f4e6 100644 --- a/Downterm/CommandNotch/AppDelegate.swift +++ b/Downterm/CommandNotch/AppDelegate.swift @@ -19,6 +19,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { observeDisplayPreference() observeSizePreferences() observeFontSizeChanges() + observeTerminalThemeChanges() } func applicationWillTerminate(_ notification: Notification) { @@ -58,6 +59,16 @@ class AppDelegate: NSObject, NSApplicationDelegate { } .store(in: &cancellables) } + + /// Live-update terminal colors across all sessions. + private func observeTerminalThemeChanges() { + UserDefaults.standard.publisher(for: \.terminalTheme) + .removeDuplicates() + .sink { newTheme in + TerminalManager.shared.updateAllThemes(TerminalTheme.resolve(newTheme)) + } + .store(in: &cancellables) + } } // MARK: - KVO key paths @@ -67,6 +78,10 @@ private extension UserDefaults { double(forKey: NotchSettings.Keys.terminalFontSize) } + @objc var terminalTheme: String { + string(forKey: NotchSettings.Keys.terminalTheme) ?? NotchSettings.Defaults.terminalTheme + } + @objc var showOnAllDisplays: Bool { bool(forKey: NotchSettings.Keys.showOnAllDisplays) } diff --git a/Downterm/CommandNotch/Models/NotchSettings.swift b/Downterm/CommandNotch/Models/NotchSettings.swift index e8c7d0b..892c258 100644 --- a/Downterm/CommandNotch/Models/NotchSettings.swift +++ b/Downterm/CommandNotch/Models/NotchSettings.swift @@ -44,6 +44,7 @@ enum NotchSettings { // Terminal static let terminalFontSize = "terminalFontSize" static let terminalShell = "terminalShell" + static let terminalTheme = "terminalTheme" // Hotkeys — each stores a HotkeyBinding JSON string static let hotkeyToggle = "hotkey_toggle" @@ -88,6 +89,7 @@ enum NotchSettings { static let terminalFontSize: Double = 13 static let terminalShell: String = "" + static let terminalTheme: String = TerminalTheme.terminalApp.rawValue // Default hotkey bindings as JSON static let hotkeyToggle: String = HotkeyBinding.cmdReturn.toJSON() @@ -133,6 +135,7 @@ enum NotchSettings { Keys.terminalFontSize: Defaults.terminalFontSize, Keys.terminalShell: Defaults.terminalShell, + Keys.terminalTheme: Defaults.terminalTheme, Keys.hotkeyToggle: Defaults.hotkeyToggle, Keys.hotkeyNewTab: Defaults.hotkeyNewTab, diff --git a/Downterm/CommandNotch/Models/TerminalManager.swift b/Downterm/CommandNotch/Models/TerminalManager.swift index 1ac4a68..6d32dbe 100644 --- a/Downterm/CommandNotch/Models/TerminalManager.swift +++ b/Downterm/CommandNotch/Models/TerminalManager.swift @@ -13,6 +13,8 @@ class TerminalManager: ObservableObject { @AppStorage(NotchSettings.Keys.terminalFontSize) private var fontSize: Double = NotchSettings.Defaults.terminalFontSize + @AppStorage(NotchSettings.Keys.terminalTheme) + private var theme: String = NotchSettings.Defaults.terminalTheme private var cancellables = Set() @@ -35,7 +37,10 @@ class TerminalManager: ObservableObject { // MARK: - Tab operations func newTab() { - let session = TerminalSession(fontSize: CGFloat(fontSize)) + let session = TerminalSession( + fontSize: CGFloat(fontSize), + theme: TerminalTheme.resolve(theme) + ) // Forward title changes to trigger view updates in this manager session.$title @@ -104,4 +109,10 @@ class TerminalManager: ObservableObject { tab.updateFontSize(size) } } + + func updateAllThemes(_ theme: TerminalTheme) { + for tab in tabs { + tab.applyTheme(theme) + } + } } diff --git a/Downterm/CommandNotch/Models/TerminalSession.swift b/Downterm/CommandNotch/Models/TerminalSession.swift index 5bdbfac..edbf98f 100644 --- a/Downterm/CommandNotch/Models/TerminalSession.swift +++ b/Downterm/CommandNotch/Models/TerminalSession.swift @@ -9,25 +9,21 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, Termina let id = UUID() let terminalView: TerminalView private var process: LocalProcess? + private let backgroundColor = NSColor.black @Published var title: String = "shell" @Published var isRunning: Bool = true @Published var currentDirectory: String? - init(fontSize: CGFloat) { + init(fontSize: CGFloat, theme: TerminalTheme) { terminalView = TerminalView(frame: NSRect(x: 0, y: 0, width: 600, height: 300)) super.init() terminalView.terminalDelegate = self - // Solid black — matches every other element in the notch. - // The single `.opacity(notchOpacity)` on ContentView makes - // everything uniformly transparent. - terminalView.nativeBackgroundColor = .black - terminalView.nativeForegroundColor = .init(white: 0.9, alpha: 1.0) - let font = NSFont.monospacedSystemFont(ofSize: fontSize, weight: .regular) terminalView.font = font + applyTheme(theme) startShell() } @@ -64,6 +60,14 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, Termina terminalView.font = NSFont.monospacedSystemFont(ofSize: size, weight: .regular) } + func applyTheme(_ theme: TerminalTheme) { + // Keep the notch visually consistent while swapping the terminal's + // default foreground color and ANSI palette for command output. + terminalView.nativeBackgroundColor = backgroundColor + terminalView.nativeForegroundColor = theme.foregroundColor + terminalView.installColors(theme.ansiColors) + } + func terminate() { process?.terminate() process = nil diff --git a/Downterm/CommandNotch/Models/TerminalTheme.swift b/Downterm/CommandNotch/Models/TerminalTheme.swift new file mode 100644 index 0000000..5749411 --- /dev/null +++ b/Downterm/CommandNotch/Models/TerminalTheme.swift @@ -0,0 +1,117 @@ +import AppKit +import SwiftTerm + +enum TerminalTheme: String, CaseIterable, Identifiable { + case terminalApp + case xterm + case solarizedDark + case dracula + case nord + + var id: String { rawValue } + + var label: String { + switch self { + case .terminalApp: return "Classic" + case .xterm: return "Xterm" + case .solarizedDark:return "Solarized Dark" + case .dracula: return "Dracula" + case .nord: return "Nord" + } + } + + var detail: String { + switch self { + case .terminalApp: + return "Matches the app's current terminal palette." + case .xterm: + return "Traditional xterm-style ANSI colors." + case .solarizedDark: + return "Low-contrast dark palette with Solarized accents." + case .dracula: + return "Higher-contrast dark palette with vivid ANSI colors." + case .nord: + return "Cool blue-grey palette with restrained accents." + } + } + + var foregroundColor: NSColor { + switch self { + case .terminalApp: + return Self.nsColor(0xE5E5E5) + case .xterm: + return Self.nsColor(0xE5E5E5) + case .solarizedDark: + return Self.nsColor(0x839496) + case .dracula: + return Self.nsColor(0xF8F8F2) + case .nord: + return Self.nsColor(0xD8DEE9) + } + } + + var ansiColors: [Color] { + switch self { + case .terminalApp: + return Self.palette([ + 0x000000, 0xC23621, 0x25BC24, 0xADAD27, + 0x492EE1, 0xD338D3, 0x33BBC8, 0xCBCCCD, + 0x818383, 0xFC391F, 0x31E722, 0xEAEC23, + 0x5833FF, 0xF935F8, 0x14F0F0, 0xE9EBEB + ]) + case .xterm: + return Self.palette([ + 0x000000, 0xCD0000, 0x00CD00, 0xCDCD00, + 0x0000EE, 0xCD00CD, 0x00CDCD, 0xE5E5E5, + 0x7F7F7F, 0xFF0000, 0x00FF00, 0xFFFF00, + 0x5C5CFF, 0xFF00FF, 0x00FFFF, 0xFFFFFF + ]) + case .solarizedDark: + return Self.palette([ + 0x073642, 0xDC322F, 0x859900, 0xB58900, + 0x268BD2, 0xD33682, 0x2AA198, 0xEEE8D5, + 0x002B36, 0xCB4B16, 0x586E75, 0x657B83, + 0x839496, 0x6C71C4, 0x93A1A1, 0xFDF6E3 + ]) + case .dracula: + return Self.palette([ + 0x21222C, 0xFF5555, 0x50FA7B, 0xF1FA8C, + 0xBD93F9, 0xFF79C6, 0x8BE9FD, 0xF8F8F2, + 0x6272A4, 0xFF6E6E, 0x69FF94, 0xFFFFA5, + 0xD6ACFF, 0xFF92DF, 0xA4FFFF, 0xFFFFFF + ]) + case .nord: + return Self.palette([ + 0x3B4252, 0xBF616A, 0xA3BE8C, 0xEBCB8B, + 0x81A1C1, 0xB48EAD, 0x88C0D0, 0xE5E9F0, + 0x4C566A, 0xBF616A, 0xA3BE8C, 0xEBCB8B, + 0x81A1C1, 0xB48EAD, 0x8FBCBB, 0xECEFF4 + ]) + } + } + + static func resolve(_ rawValue: String) -> TerminalTheme { + TerminalTheme(rawValue: rawValue) ?? .terminalApp + } + + private static func palette(_ hexValues: [UInt32]) -> [Color] { + hexValues.map(terminalColor) + } + + private static func terminalColor(_ hex: UInt32) -> Color { + Color( + red: UInt16(((hex >> 16) & 0xFF) * 257), + green: UInt16(((hex >> 8) & 0xFF) * 257), + blue: UInt16((hex & 0xFF) * 257) + ) + } + + private static func nsColor(_ hex: UInt32) -> NSColor { + NSColor( + deviceRed: CGFloat((hex >> 16) & 0xFF) / 255.0, + green: CGFloat((hex >> 8) & 0xFF) / 255.0, + blue: CGFloat(hex & 0xFF) / 255.0, + alpha: 1.0 + ) + } +} diff --git a/Downterm/CommandNotch/Views/SettingsView.swift b/Downterm/CommandNotch/Views/SettingsView.swift index 20514a6..fa33e86 100644 --- a/Downterm/CommandNotch/Views/SettingsView.swift +++ b/Downterm/CommandNotch/Views/SettingsView.swift @@ -265,6 +265,7 @@ struct TerminalSettingsView: View { @AppStorage(NotchSettings.Keys.terminalFontSize) private var fontSize = NotchSettings.Defaults.terminalFontSize @AppStorage(NotchSettings.Keys.terminalShell) private var shellPath = NotchSettings.Defaults.terminalShell + @AppStorage(NotchSettings.Keys.terminalTheme) private var theme = NotchSettings.Defaults.terminalTheme var body: some View { Form { @@ -275,6 +276,21 @@ struct TerminalSettingsView: View { Text("\(Int(fontSize))pt").monospacedDigit().frame(width: 50) } } + Section("Colors") { + Picker("Theme", selection: $theme) { + ForEach(TerminalTheme.allCases) { terminalTheme in + Text(terminalTheme.label).tag(terminalTheme.rawValue) + } + } + + Text(TerminalTheme.resolve(theme).detail) + .font(.caption) + .foregroundStyle(.secondary) + + Text("Applies to normal terminal text and the ANSI palette used by tools like `ls`.") + .font(.caption) + .foregroundStyle(.secondary) + } Section("Shell") { TextField("Shell path (empty = $SHELL)", text: $shellPath) .textFieldStyle(.roundedBorder)