From 303c9142dc54c44600aab7e320b329a7b5054b21 Mon Sep 17 00:00:00 2001 From: Jon Parise Date: Wed, 18 Feb 2026 11:47:30 -0500 Subject: [PATCH] macos: improve "Set Default Terminal" Switch to using the existing UTType.unixExecutable constant for this operator, which also lets us remove a failure path. Also, use the completion-based setDefaultApplication() variant to handle errors. This simplifies the code enough that we don't need the additional NSWorkspace+Ghostty extension functions. --- macos/Ghostty.xcodeproj/project.pbxproj | 1 - macos/Sources/App/macOS/AppDelegate.swift | 33 +++++++++--------- .../Extensions/NSWorkspace+Ghostty.swift | 34 ------------------- .../Extensions/NSWorkspace+Extension.swift | 9 +++-- 4 files changed, 23 insertions(+), 54 deletions(-) delete mode 100644 macos/Sources/Ghostty/Extensions/NSWorkspace+Ghostty.swift diff --git a/macos/Ghostty.xcodeproj/project.pbxproj b/macos/Ghostty.xcodeproj/project.pbxproj index fcd84e266..ab6dde118 100644 --- a/macos/Ghostty.xcodeproj/project.pbxproj +++ b/macos/Ghostty.xcodeproj/project.pbxproj @@ -147,7 +147,6 @@ Features/Update/UpdatePopoverView.swift, Features/Update/UpdateSimulator.swift, Features/Update/UpdateViewModel.swift, - "Ghostty/Extensions/NSWorkspace+Ghostty.swift", "Ghostty/FullscreenMode+Extension.swift", Ghostty/Ghostty.Error.swift, Ghostty/Ghostty.Event.swift, diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index c0f739a2d..2ca7d4813 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -1294,19 +1294,21 @@ extension AppDelegate { ud.removeObject(forKey: key) } } - + @IBAction func setAsDefaultTerminal(_ sender: NSMenuItem) { - do { - try NSWorkspace.shared.setGhosttyAsDefaultTerminal() - // Success - menu state will automatically update via validateMenuItem - } catch { - // Show error dialog - let alert = NSAlert() - alert.messageText = "Failed to Set Default Terminal" - alert.informativeText = "Ghostty could not be set as the default terminal application.\n\nError: \(error.localizedDescription)" - alert.alertStyle = .warning - alert.addButton(withTitle: "OK") - alert.runModal() + NSWorkspace.shared.setDefaultApplication(at: Bundle.main.bundleURL, toOpen: .unixExecutable) { error in + guard let error else { return } + Task { @MainActor in + let alert = NSAlert() + alert.messageText = "Failed to Set Default Terminal" + alert.informativeText = """ + Ghostty could not be set as the default terminal application. + + Error: \(error.localizedDescription) + """ + alert.alertStyle = .warning + alert.runModal() + } } } } @@ -1317,11 +1319,8 @@ extension AppDelegate: NSMenuItemValidation { func validateMenuItem(_ item: NSMenuItem) -> Bool { switch item.action { case #selector(setAsDefaultTerminal(_:)): - // Check if Ghostty is already the default terminal - let isDefault = NSWorkspace.shared.isGhosttyDefaultTerminal - // Disable menu item if already default (option A) - return !isDefault - + return NSWorkspace.shared.defaultTerminal != Bundle.main.bundleURL + case #selector(floatOnTop(_:)), #selector(useAsDefault(_:)): // Float on top items only active if the key window is a primary diff --git a/macos/Sources/Ghostty/Extensions/NSWorkspace+Ghostty.swift b/macos/Sources/Ghostty/Extensions/NSWorkspace+Ghostty.swift deleted file mode 100644 index e16169062..000000000 --- a/macos/Sources/Ghostty/Extensions/NSWorkspace+Ghostty.swift +++ /dev/null @@ -1,34 +0,0 @@ -import AppKit -import UniformTypeIdentifiers - -extension NSWorkspace { - /// Checks if Ghostty is the default terminal application. - /// - Returns: True if Ghostty is the default application for handling public.unix-executable files. - var isGhosttyDefaultTerminal: Bool { - let ghosttyURL = Bundle.main.bundleURL - guard let defaultAppURL = defaultApplicationURL(forContentType: "public.unix-executable") else { - return false - } - // Compare bundle paths - return ghosttyURL.path == defaultAppURL.path - } - - /// Sets Ghostty as the default terminal application. - /// - Throws: An error if the application bundle cannot be located or if setting the default fails. - func setGhosttyAsDefaultTerminal() throws { - let ghosttyURL = Bundle.main.bundleURL - - // Create UTType for unix executables - guard let unixExecutableType = UTType("public.unix-executable") else { - throw NSError( - domain: "com.mitchellh.ghostty", - code: 2, - userInfo: [NSLocalizedDescriptionKey: "Could not create UTType for public.unix-executable"] - ) - } - - // Use NSWorkspace API to set the default application - // This API is available on macOS 12.0+, Ghostty supports 13.0+, so it's compatible - setDefaultApplication(at: ghosttyURL, toOpen: unixExecutableType) - } -} diff --git a/macos/Sources/Helpers/Extensions/NSWorkspace+Extension.swift b/macos/Sources/Helpers/Extensions/NSWorkspace+Extension.swift index 809c927c7..e87e0676c 100644 --- a/macos/Sources/Helpers/Extensions/NSWorkspace+Extension.swift +++ b/macos/Sources/Helpers/Extensions/NSWorkspace+Extension.swift @@ -7,7 +7,13 @@ extension NSWorkspace { var defaultTextEditor: URL? { defaultApplicationURL(forContentType: UTType.plainText.identifier) } - + + /// Returns the URL of the default terminal (Unix Executable) application. + /// - Returns: The URL of the default terminal, or nil if no default terminal is found. + var defaultTerminal: URL? { + defaultApplicationURL(forContentType: UTType.unixExecutable.identifier) + } + /// Returns the URL of the default application for opening files with the specified content type. /// - Parameter contentType: The content type identifier (UTI) to find the default application for. /// - Returns: The URL of the default application, or nil if no default application is found. @@ -26,5 +32,4 @@ extension NSWorkspace { guard let uti = UTType(filenameExtension: ext) else { return nil} return defaultApplicationURL(forContentType: uti.identifier) } - }