From 2c28c27ca52f130fa743ae3314cdbb0cd5ebd710 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 23 Feb 2026 13:14:49 -0800 Subject: [PATCH] moving lots of files, removing unused stuff --- macos/Ghostty.xcodeproj/project.pbxproj | 40 ++-- macos/Sources/App/macOS/AppDelegate.swift | 5 +- macos/Sources/App/macOS/AppIcon.swift | 187 ------------------ .../Features/Dock Tile Plugin/AppIcon.swift | 144 ++++++++++++++ .../Dock Tile Plugin}/DockTilePlugin.swift | 29 +-- .../Notification+AppIcon.swift | 5 + .../UserDefaults+AppIcon.swift | 37 ++++ ...ackage.swift => Ghostty.ConfigTypes.swift} | 21 +- .../{Package.swift => GhosttyPackage.swift} | 8 + .../Sources/Ghostty/GhosttyPackageMeta.swift | 16 ++ 10 files changed, 248 insertions(+), 244 deletions(-) delete mode 100644 macos/Sources/App/macOS/AppIcon.swift create mode 100644 macos/Sources/Features/Dock Tile Plugin/AppIcon.swift rename macos/{DockTilePlugIn => Sources/Features/Dock Tile Plugin}/DockTilePlugin.swift (85%) create mode 100644 macos/Sources/Features/Dock Tile Plugin/Notification+AppIcon.swift create mode 100644 macos/Sources/Features/Dock Tile Plugin/UserDefaults+AppIcon.swift rename macos/Sources/Ghostty/{SharedPackage.swift => Ghostty.ConfigTypes.swift} (65%) rename macos/Sources/Ghostty/{Package.swift => GhosttyPackage.swift} (98%) create mode 100644 macos/Sources/Ghostty/GhosttyPackageMeta.swift diff --git a/macos/Ghostty.xcodeproj/project.pbxproj b/macos/Ghostty.xcodeproj/project.pbxproj index 17c6f46e6..27f9d0902 100644 --- a/macos/Ghostty.xcodeproj/project.pbxproj +++ b/macos/Ghostty.xcodeproj/project.pbxproj @@ -97,9 +97,13 @@ 8193245D2F24E80800A9ED8F /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( - App/macOS/AppIcon.swift, "Features/Colorized Ghostty Icon/ColorizedGhosttyIcon.swift", - Ghostty/SharedPackage.swift, + "Features/Dock Tile Plugin/AppIcon.swift", + "Features/Dock Tile Plugin/DockTilePlugin.swift", + "Features/Dock Tile Plugin/Notification+AppIcon.swift", + "Features/Dock Tile Plugin/UserDefaults+AppIcon.swift", + Ghostty/Ghostty.ConfigTypes.swift, + Ghostty/GhosttyPackageMeta.swift, Helpers/CrossKit.swift, "Helpers/Extensions/NSImage+Extension.swift", "Helpers/Extensions/OSColor+Extension.swift", @@ -111,7 +115,6 @@ membershipExceptions = ( App/macOS/AppDelegate.swift, "App/macOS/AppDelegate+Ghostty.swift", - App/macOS/AppIcon.swift, App/macOS/main.swift, App/macOS/MainMenu.xib, Features/About/About.xib, @@ -139,6 +142,10 @@ "Features/Colorized Ghostty Icon/ColorizedGhosttyIconView.swift", "Features/Command Palette/CommandPalette.swift", "Features/Command Palette/TerminalCommandPalette.swift", + "Features/Dock Tile Plugin/AppIcon.swift", + "Features/Dock Tile Plugin/DockTilePlugin.swift", + "Features/Dock Tile Plugin/Notification+AppIcon.swift", + "Features/Dock Tile Plugin/UserDefaults+AppIcon.swift", "Features/Global Keybinds/GlobalEventTap.swift", Features/QuickTerminal/QuickTerminal.xib, Features/QuickTerminal/QuickTerminalController.swift, @@ -238,6 +245,7 @@ isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( App/iOS/iOSApp.swift, + "Features/Dock Tile Plugin/DockTilePlugin.swift", "Ghostty/Surface View/SurfaceView_UIKit.swift", ); target = A5B30530299BEAAA0047F10C /* Ghostty */; @@ -246,7 +254,6 @@ /* Begin PBXFileSystemSynchronizedRootGroup section */ 810ACCA02E9D3302004F8F92 /* GhosttyUITests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = GhosttyUITests; sourceTree = ""; }; - 8193245A2F24E7D000A9ED8F /* DockTilePlugIn */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = DockTilePlugIn; sourceTree = ""; }; 81F82BC72E82815D001EDFA7 /* Sources */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (81F82CB12E8281F9001EDFA7 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 81F82CB02E8281F5001EDFA7 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 8193245D2F24E80800A9ED8F /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Sources; sourceTree = ""; }; A54F45F42E1F047A0046BD5C /* Tests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Tests; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ @@ -321,7 +328,6 @@ A51BFC282B30F26D00E92F16 /* GhosttyDebug.entitlements */, 3B39CAA42B33949B00DABEB8 /* GhosttyReleaseLocal.entitlements */, 81F82BC72E82815D001EDFA7 /* Sources */, - 8193245A2F24E7D000A9ED8F /* DockTilePlugIn */, A54F45F42E1F047A0046BD5C /* Tests */, 810ACCA02E9D3302004F8F92 /* GhosttyUITests */, A5D495A3299BECBA00DD1313 /* Frameworks */, @@ -389,9 +395,6 @@ ); dependencies = ( ); - fileSystemSynchronizedGroups = ( - 8193245A2F24E7D000A9ED8F /* DockTilePlugIn */, - ); name = DockTilePlugin; packageProductDependencies = ( ); @@ -845,13 +848,14 @@ ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "Ghostty Dock Tile Plugin"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSPrincipalClass = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 26.2; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.mitchellh.DockTilePlugin; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 0.1; + PRODUCT_BUNDLE_IDENTIFIER = "com.mitchellh.ghostty-dock-tile"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; @@ -873,13 +877,14 @@ ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "Ghostty Dock Tile Plugin"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSPrincipalClass = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 26.2; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.mitchellh.DockTilePlugin; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 0.1; + PRODUCT_BUNDLE_IDENTIFIER = "com.mitchellh.ghostty-dock-tile"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; @@ -901,13 +906,14 @@ ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "Ghostty Dock Tile Plugin"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSPrincipalClass = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 26.2; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.mitchellh.DockTilePlugin; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 0.1; + PRODUCT_BUNDLE_IDENTIFIER = "com.mitchellh.ghostty-dock-tile"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index 2b2c1b7d1..b9aab0ac4 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -928,6 +928,7 @@ class AppDelegate: NSObject, } else { GlobalEventTap.shared.disable() } + updateAppIcon(from: config) } @@ -941,9 +942,9 @@ class AppDelegate: NSObject, // clean it up here to trigger a correct update of the current config. UserDefaults.standard.removeObject(forKey: "CustomGhosttyIcon") DispatchQueue.global().async { - UserDefaults.standard.appIcon = Ghostty.CustomAppIcon(config: config) + UserDefaults.standard.appIcon = AppIcon(config: config) DistributedNotificationCenter.default() - .postNotificationName(Ghostty.Notification.ghosttyIconDidChange, object: nil, userInfo: nil, deliverImmediately: true) + .postNotificationName(.ghosttyIconDidChange, object: nil, userInfo: nil, deliverImmediately: true) } } diff --git a/macos/Sources/App/macOS/AppIcon.swift b/macos/Sources/App/macOS/AppIcon.swift deleted file mode 100644 index 3dd8cdd98..000000000 --- a/macos/Sources/App/macOS/AppIcon.swift +++ /dev/null @@ -1,187 +0,0 @@ -import AppKit -import System - -#if !DOCK_TILE_PLUGIN -import GhosttyKit -#endif - -extension Ghostty { - /// For DockTilePlugin to generate icon - /// without relying on ``Ghostty/Ghostty/Config`` - enum CustomAppIcon: Equatable, Codable { - case official - case blueprint - case chalkboard - case glass - case holographic - case microchip - case paper - case retro - case xray - /// Save image data to avoid sandboxing issues - case custom(fileData: Data) - case customStyle(ghostColorHex: String, screenColorHexes: [String], iconFrame: Ghostty.MacOSIconFrame) - - /// Restore the icon from previously saved values - init?(string: String) { - switch string { - case MacOSIcon.official.rawValue: - self = .official - case MacOSIcon.blueprint.rawValue: - self = .blueprint - case MacOSIcon.chalkboard.rawValue: - self = .chalkboard - case MacOSIcon.glass.rawValue: - self = .glass - case MacOSIcon.holographic.rawValue: - self = .holographic - case MacOSIcon.microchip.rawValue: - self = .microchip - case MacOSIcon.paper.rawValue: - self = .paper - case MacOSIcon.retro.rawValue: - self = .retro - case MacOSIcon.xray.rawValue: - self = .xray - default: - /* - let colorStrings = ([ghostColor] + screenColors).compactMap(\.hexString) - appIconName = (colorStrings + [config.macosIconFrame.rawValue]) - .joined(separator: "_") - */ - var parts = string.split(separator: "_").map(String.init) - if - let _ = parts.first.flatMap(NSColor.init(hex:)), - let frame = parts.last.flatMap(Ghostty.MacOSIconFrame.init(rawValue:)) - { - let ghostC = parts.removeFirst() - _ = parts.removeLast() - self = .customStyle( - ghostColorHex: ghostC, - screenColorHexes: parts, - iconFrame: frame - ) - } else { - // Due to sandboxing with `com.apple.dock.external.extra.arm64`, - // we can’t restore custom icon file automatically. - // The user must open the app to update it. - return nil - } - } - } - - func image(in bundle: Bundle) -> NSImage? { - switch self { - case .official: - return nil - case .blueprint: - return bundle.image(forResource: "BlueprintImage")! - case .chalkboard: - return bundle.image(forResource: "ChalkboardImage")! - case .glass: - return bundle.image(forResource: "GlassImage")! - case .holographic: - return bundle.image(forResource: "HolographicImage")! - case .microchip: - return bundle.image(forResource: "MicrochipImage")! - case .paper: - return bundle.image(forResource: "PaperImage")! - case .retro: - return bundle.image(forResource: "RetroImage")! - case .xray: - return bundle.image(forResource: "XrayImage")! - case let .custom(file): - if let userIcon = NSImage(data: file) { - return userIcon - } else { - return nil - } - case let .customStyle(ghostColorHex, screenColorHexes, macosIconFrame): - let screenColors = screenColorHexes.compactMap(NSColor.init(hex:)) - guard - let ghostColor = NSColor(hex: ghostColorHex), - let icon = ColorizedGhosttyIcon( - screenColors: screenColors, - ghostColor: ghostColor, - frame: macosIconFrame - ).makeImage(in: bundle) - else { - return nil - } - return icon - } - } - } -} - -#if !DOCK_TILE_PLUGIN -extension Ghostty.CustomAppIcon { - init?(config: Ghostty.Config) { - switch config.macosIcon { - case .official: - return nil - case .blueprint: - self = .blueprint - case .chalkboard: - self = .chalkboard - case .glass: - self = .glass - case .holographic: - self = .holographic - case .microchip: - self = .microchip - case .paper: - self = .paper - case .retro: - self = .retro - case .xray: - self = .xray - case .custom: - if let data = try? Data(contentsOf: URL(filePath: config.macosCustomIcon, relativeTo: nil)) { - self = .custom(fileData: data) - } else { - return nil - } - case .customStyle: - // Discard saved icon name - // if no valid colours were found - guard - let ghostColor = config.macosIconGhostColor?.hexString, - let screenColors = config.macosIconScreenColor?.compactMap(\.hexString) - else { - return nil - } - self = .customStyle(ghostColorHex: ghostColor, screenColorHexes: screenColors, iconFrame: config.macosIconFrame) - } - } -} -#endif - -extension UserDefaults { - var appIcon: Ghostty.CustomAppIcon? { - get { - defer { - removeObject(forKey: "CustomGhosttyIcon") - } - if let previous = string(forKey: "CustomGhosttyIcon"), let newIcon = Ghostty.CustomAppIcon(string: previous) { - // update new storage once - self.appIcon = newIcon - return newIcon - } - guard let data = data(forKey: "NewCustomGhosttyIcon") else { - return nil - } - return try? JSONDecoder().decode(Ghostty.CustomAppIcon.self, from: data) - } - set { - guard let newData = try? JSONEncoder().encode(newValue) else { - return - } - set(newData, forKey: "NewCustomGhosttyIcon") - } - } -} - -extension Ghostty.Notification { - static let ghosttyIconDidChange = Notification.Name("com.mitchellh.ghostty.iconDidChange") -} diff --git a/macos/Sources/Features/Dock Tile Plugin/AppIcon.swift b/macos/Sources/Features/Dock Tile Plugin/AppIcon.swift new file mode 100644 index 000000000..52ab9ef95 --- /dev/null +++ b/macos/Sources/Features/Dock Tile Plugin/AppIcon.swift @@ -0,0 +1,144 @@ +import AppKit +import System + +/// The icon style for the Ghostty App. +enum AppIcon: Equatable, Codable { + case official + case blueprint + case chalkboard + case glass + case holographic + case microchip + case paper + case retro + case xray + /// Save full image data to avoid sandboxing issues + case custom(fileData: Data) + case customStyle(ghostColorHex: String, screenColorHexes: [String], iconFrame: Ghostty.MacOSIconFrame) + +#if !DOCK_TILE_PLUGIN + init?(config: Ghostty.Config) { + switch config.macosIcon { + case .official: + return nil + case .blueprint: + self = .blueprint + case .chalkboard: + self = .chalkboard + case .glass: + self = .glass + case .holographic: + self = .holographic + case .microchip: + self = .microchip + case .paper: + self = .paper + case .retro: + self = .retro + case .xray: + self = .xray + case .custom: + if let data = try? Data(contentsOf: URL(filePath: config.macosCustomIcon, relativeTo: nil)) { + self = .custom(fileData: data) + } else { + return nil + } + case .customStyle: + // Discard saved icon name + // if no valid colours were found + guard + let ghostColor = config.macosIconGhostColor?.hexString, + let screenColors = config.macosIconScreenColor?.compactMap(\.hexString) + else { + return nil + } + self = .customStyle(ghostColorHex: ghostColor, screenColorHexes: screenColors, iconFrame: config.macosIconFrame) + } + } +#endif + + /// Restore the icon from previously saved values + init?(string: String) { + switch string { + case Ghostty.MacOSIcon.official.rawValue: + self = .official + case Ghostty.MacOSIcon.blueprint.rawValue: + self = .blueprint + case Ghostty.MacOSIcon.chalkboard.rawValue: + self = .chalkboard + case Ghostty.MacOSIcon.glass.rawValue: + self = .glass + case Ghostty.MacOSIcon.holographic.rawValue: + self = .holographic + case Ghostty.MacOSIcon.microchip.rawValue: + self = .microchip + case Ghostty.MacOSIcon.paper.rawValue: + self = .paper + case Ghostty.MacOSIcon.retro.rawValue: + self = .retro + case Ghostty.MacOSIcon.xray.rawValue: + self = .xray + default: + var parts = string.split(separator: "_").map(String.init) + if + let _ = parts.first.flatMap(NSColor.init(hex:)), + let frame = parts.last.flatMap(Ghostty.MacOSIconFrame.init(rawValue:)) + { + let ghostC = parts.removeFirst() + _ = parts.removeLast() + self = .customStyle( + ghostColorHex: ghostC, + screenColorHexes: parts, + iconFrame: frame + ) + } else { + // Due to sandboxing with `com.apple.dock.external.extra.arm64`, + // we can’t restore custom icon file automatically. + // The user must open the app to update it. + return nil + } + } + } + + func image(in bundle: Bundle) -> NSImage? { + switch self { + case .official: + return nil + case .blueprint: + return bundle.image(forResource: "BlueprintImage")! + case .chalkboard: + return bundle.image(forResource: "ChalkboardImage")! + case .glass: + return bundle.image(forResource: "GlassImage")! + case .holographic: + return bundle.image(forResource: "HolographicImage")! + case .microchip: + return bundle.image(forResource: "MicrochipImage")! + case .paper: + return bundle.image(forResource: "PaperImage")! + case .retro: + return bundle.image(forResource: "RetroImage")! + case .xray: + return bundle.image(forResource: "XrayImage")! + case let .custom(file): + if let userIcon = NSImage(data: file) { + return userIcon + } else { + return nil + } + case let .customStyle(ghostColorHex, screenColorHexes, macosIconFrame): + let screenColors = screenColorHexes.compactMap(NSColor.init(hex:)) + guard + let ghostColor = NSColor(hex: ghostColorHex), + let icon = ColorizedGhosttyIcon( + screenColors: screenColors, + ghostColor: ghostColor, + frame: macosIconFrame + ).makeImage(in: bundle) + else { + return nil + } + return icon + } + } +} diff --git a/macos/DockTilePlugIn/DockTilePlugin.swift b/macos/Sources/Features/Dock Tile Plugin/DockTilePlugin.swift similarity index 85% rename from macos/DockTilePlugIn/DockTilePlugin.swift rename to macos/Sources/Features/Dock Tile Plugin/DockTilePlugin.swift index f711bfd2d..ad750376d 100644 --- a/macos/DockTilePlugIn/DockTilePlugin.swift +++ b/macos/Sources/Features/Dock Tile Plugin/DockTilePlugin.swift @@ -1,10 +1,14 @@ import AppKit -/// This class lives as long as the app is in the Dock. -/// If the user pins the app to the Dock, it will not be deallocated. -/// Be careful when storing state in this class. class DockTilePlugin: NSObject, NSDockTilePlugIn { + // WARNING: An instance of this class is alive as long as Ghostty's icon is + // in the doc (running or not!), so keep any state and processing to a + // minimum to respect resource usage. + private let pluginBundle = Bundle(for: DockTilePlugin.self) + + // Separate defaults based on debug vs release builds so we can test icons + // without messing up releases. #if DEBUG private let ghosttyUserDefaults = UserDefaults(suiteName: "com.mitchellh.ghostty.debug") #else @@ -21,7 +25,7 @@ class DockTilePlugin: NSObject, NSDockTilePlugIn { // Try to restore the previous icon on launch. iconDidChange(ghosttyUserDefaults.appIcon, dockTile: dockTile) - iconChangeObserver = DistributedNotificationCenter.default().publisher(for: Ghostty.Notification.ghosttyIconDidChange) + iconChangeObserver = DistributedNotificationCenter.default().publisher(for: .ghosttyIconDidChange) .map { [weak self] _ in self?.ghosttyUserDefaults?.appIcon } @@ -41,7 +45,7 @@ class DockTilePlugin: NSObject, NSDockTilePlugIn { return url.path } - func iconDidChange(_ newIcon: Ghostty.CustomAppIcon?, dockTile: NSDockTile) { + func iconDidChange(_ newIcon: AppIcon?, dockTile: NSDockTile) { guard let appIcon = newIcon?.image(in: pluginBundle) else { resetIcon(dockTile: dockTile) return @@ -80,6 +84,7 @@ class DockTilePlugin: NSObject, NSDockTilePlugIn { appIcon = pluginBundle.image(forResource: "AppIconImage")! NSWorkspace.shared.setIcon(appIcon, forFile: appBundlePath) } + NSWorkspace.shared.noteFileSystemChanged(appBundlePath) dockTile.setIcon(appIcon) } @@ -99,17 +104,3 @@ private extension NSDockTile { } extension NSDockTile: @unchecked @retroactive Sendable {} - -#if DEBUG -private extension NSAlert { - static func notify(_ message: String, image: NSImage?) { - DispatchQueue.main.async { - let alert = NSAlert() - alert.messageText = message - alert.icon = image - _ = alert.runModal() - } - } -} -#endif - diff --git a/macos/Sources/Features/Dock Tile Plugin/Notification+AppIcon.swift b/macos/Sources/Features/Dock Tile Plugin/Notification+AppIcon.swift new file mode 100644 index 000000000..e492f1a77 --- /dev/null +++ b/macos/Sources/Features/Dock Tile Plugin/Notification+AppIcon.swift @@ -0,0 +1,5 @@ +import AppKit + +extension Notification.Name { + static let ghosttyIconDidChange = Notification.Name("com.mitchellh.ghostty.iconDidChange") +} diff --git a/macos/Sources/Features/Dock Tile Plugin/UserDefaults+AppIcon.swift b/macos/Sources/Features/Dock Tile Plugin/UserDefaults+AppIcon.swift new file mode 100644 index 000000000..cce8e24a4 --- /dev/null +++ b/macos/Sources/Features/Dock Tile Plugin/UserDefaults+AppIcon.swift @@ -0,0 +1,37 @@ +import AppKit + +extension UserDefaults { + private static let customIconKeyOld = "CustomGhosttyIcon" + private static let customIconKeyNew = "CustomGhosttyIcon2" + + var appIcon: AppIcon? { + get { + // Always remove our old pre-docktileplugin values. + defer { + removeObject(forKey: Self.customIconKeyOld) + } + + // If we have an old, pre-docktileplugin value, then we parse the + // the old value (try) and set it. + if let previous = string(forKey: Self.customIconKeyOld), let newIcon = AppIcon(string: previous) { + // update new storage once + self.appIcon = newIcon + return newIcon + } + + // Check if we have the new key for our dock tile plugin format. + guard let data = data(forKey: Self.customIconKeyNew) else { + return nil + } + return try? JSONDecoder().decode(AppIcon.self, from: data) + } + + set { + guard let newData = try? JSONEncoder().encode(newValue) else { + return + } + + set(newData, forKey: Self.customIconKeyNew) + } + } +} diff --git a/macos/Sources/Ghostty/SharedPackage.swift b/macos/Sources/Ghostty/Ghostty.ConfigTypes.swift similarity index 65% rename from macos/Sources/Ghostty/SharedPackage.swift rename to macos/Sources/Ghostty/Ghostty.ConfigTypes.swift index 3e591a57b..8c559fad2 100644 --- a/macos/Sources/Ghostty/SharedPackage.swift +++ b/macos/Sources/Ghostty/Ghostty.ConfigTypes.swift @@ -1,22 +1,5 @@ -import Foundation -import os - -enum Ghostty { - // The primary logger used by the GhosttyKit libraries. - static let logger = Logger( - subsystem: Bundle.main.bundleIdentifier!, - category: "ghostty" - ) - - // All the notifications that will be emitted will be put here. - struct Notification {} - - // The user notification category identifier - static let userNotificationCategory = "com.mitchellh.ghostty.userNotification" - - // The user notification "Show" action - static let userNotificationActionShow = "com.mitchellh.ghostty.userNotification.Show" -} +// This file contains the configuration types for Ghostty so that alternate targets +// can get typed information without depending on all the dependencies of GhosttyKit. extension Ghostty { /// macos-icon diff --git a/macos/Sources/Ghostty/Package.swift b/macos/Sources/Ghostty/GhosttyPackage.swift similarity index 98% rename from macos/Sources/Ghostty/Package.swift rename to macos/Sources/Ghostty/GhosttyPackage.swift index 30c355327..03211862f 100644 --- a/macos/Sources/Ghostty/Package.swift +++ b/macos/Sources/Ghostty/GhosttyPackage.swift @@ -11,6 +11,14 @@ extension ghostty_command_s: @unchecked @retroactive Sendable {} /// may be unsafe but the value itself is safe to send across threads. extension ghostty_surface_t: @unchecked @retroactive Sendable {} +extension Ghostty { + // The user notification category identifier + static let userNotificationCategory = "com.mitchellh.ghostty.userNotification" + + // The user notification "Show" action + static let userNotificationActionShow = "com.mitchellh.ghostty.userNotification.Show" +} + // MARK: Build Info extension Ghostty { diff --git a/macos/Sources/Ghostty/GhosttyPackageMeta.swift b/macos/Sources/Ghostty/GhosttyPackageMeta.swift new file mode 100644 index 000000000..8e035c323 --- /dev/null +++ b/macos/Sources/Ghostty/GhosttyPackageMeta.swift @@ -0,0 +1,16 @@ +import Foundation +import os + +// This defines the minimal information required so all other files can do +// `extension Ghostty` to add more to it. This purposely has minimal +// dependencies so things like our dock tile plugin can use it. +enum Ghostty { + // The primary logger used by the GhosttyKit libraries. + static let logger = Logger( + subsystem: Bundle.main.bundleIdentifier!, + category: "ghostty" + ) + + // All the notifications that will be emitted will be put here. + struct Notification {} +}