mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
macos: DockTilePlugin finds app bundle via .app suffix
Fixes #11029 (probably) If you renamed the app bundle, the prior check would infinite loop due to the combination of two bugs: invalid termination checks and hardcoding "Ghostty.app"
This commit is contained in:
@@ -17,14 +17,30 @@ class DockTilePlugin: NSObject, NSDockTilePlugIn {
|
||||
|
||||
private var iconChangeObserver: Any?
|
||||
|
||||
/// The path to the Ghostty.app, determined based on the bundle path of this plugin.
|
||||
var ghosttyAppPath: String {
|
||||
var url = pluginBundle.bundleURL
|
||||
// Remove "/Contents/PlugIns/DockTilePlugIn.bundle" from the bundle URL to reach Ghostty.app.
|
||||
while url.lastPathComponent != "Ghostty.app", !url.lastPathComponent.isEmpty {
|
||||
url.deleteLastPathComponent()
|
||||
/// The URL to the enclosing app bundle, determined from the plugin bundle path.
|
||||
var ghosttyAppURL: URL? {
|
||||
Self.appBundleURL(for: pluginBundle.bundleURL)
|
||||
}
|
||||
|
||||
/// Determine the enclosing app bundle for the dock tile plugin bundle.
|
||||
///
|
||||
/// We intentionally avoid matching a specific bundle name (such as
|
||||
/// "Ghostty.app") so renaming the app in Finder still works.
|
||||
static func appBundleURL(for pluginBundleURL: URL) -> URL? {
|
||||
var url = pluginBundleURL
|
||||
while true {
|
||||
if url.pathExtension.compare("app", options: .caseInsensitive) == .orderedSame {
|
||||
return url
|
||||
}
|
||||
|
||||
let parent = url.deletingLastPathComponent()
|
||||
if parent.path == url.path {
|
||||
// Safety stop: this should only happen at filesystem root.
|
||||
return nil
|
||||
}
|
||||
|
||||
url = parent
|
||||
}
|
||||
return url.path
|
||||
}
|
||||
|
||||
/// The primary NSDockTilePlugin function.
|
||||
@@ -54,27 +70,32 @@ class DockTilePlugin: NSObject, NSDockTilePlugIn {
|
||||
return
|
||||
}
|
||||
|
||||
let appBundlePath = self.ghosttyAppPath
|
||||
NSWorkspace.shared.setIcon(appIcon, forFile: appBundlePath)
|
||||
NSWorkspace.shared.noteFileSystemChanged(appBundlePath)
|
||||
if let appBundleURL = self.ghosttyAppURL {
|
||||
let appBundlePath = appBundleURL.path
|
||||
NSWorkspace.shared.setIcon(appIcon, forFile: appBundlePath)
|
||||
NSWorkspace.shared.noteFileSystemChanged(appBundlePath)
|
||||
}
|
||||
|
||||
dockTile.setIcon(appIcon)
|
||||
}
|
||||
|
||||
/// Reset the application icon and dock tile icon to the default.
|
||||
private func resetIcon(dockTile: NSDockTile) {
|
||||
let appBundlePath = self.ghosttyAppPath
|
||||
let appBundlePath = self.ghosttyAppURL?.path
|
||||
let appIcon: NSImage
|
||||
if #available(macOS 26.0, *) {
|
||||
// Reset to the default (glassy) icon.
|
||||
NSWorkspace.shared.setIcon(nil, forFile: appBundlePath)
|
||||
if let appBundlePath {
|
||||
NSWorkspace.shared.setIcon(nil, forFile: appBundlePath)
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// Use the `Blueprint` icon to distinguish Debug from Release builds.
|
||||
appIcon = pluginBundle.image(forResource: "BlueprintImage")!
|
||||
#else
|
||||
// Get the composed icon from the app bundle.
|
||||
if let iconRep = NSWorkspace.shared.icon(forFile: appBundlePath)
|
||||
if let appBundlePath,
|
||||
let iconRep = NSWorkspace.shared.icon(forFile: appBundlePath)
|
||||
.bestRepresentation(
|
||||
for: CGRect(origin: .zero, size: dockTile.size),
|
||||
context: nil,
|
||||
@@ -91,11 +112,15 @@ class DockTilePlugin: NSObject, NSDockTilePlugIn {
|
||||
} else {
|
||||
// Use the bundled icon to keep the corner radius consistent with pre-Tahoe apps.
|
||||
appIcon = pluginBundle.image(forResource: "AppIconImage")!
|
||||
NSWorkspace.shared.setIcon(appIcon, forFile: appBundlePath)
|
||||
if let appBundlePath {
|
||||
NSWorkspace.shared.setIcon(appIcon, forFile: appBundlePath)
|
||||
}
|
||||
}
|
||||
|
||||
// Notify Finder/Dock so icon caches refresh immediately.
|
||||
NSWorkspace.shared.noteFileSystemChanged(appBundlePath)
|
||||
if let appBundlePath {
|
||||
NSWorkspace.shared.noteFileSystemChanged(appBundlePath)
|
||||
}
|
||||
dockTile.setIcon(appIcon)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user