mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-14 03:25:50 +00:00
add "Set Ghostty as Default Terminal App" on macOS (#10810)
This PR enables iTerm2-like one button "Set Ghostty as Default Terminal App" functionality on macOS, making it easier to open a directory in Ghostty, run shell scripts when mouse clicking, etc.
This commit is contained in:
@@ -147,6 +147,7 @@
|
||||
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,
|
||||
|
||||
@@ -65,6 +65,7 @@ class AppDelegate: NSObject,
|
||||
@IBOutlet private var menuReturnToDefaultSize: NSMenuItem?
|
||||
@IBOutlet private var menuFloatOnTop: NSMenuItem?
|
||||
@IBOutlet private var menuUseAsDefault: NSMenuItem?
|
||||
@IBOutlet private var menuSetAsDefaultTerminal: NSMenuItem?
|
||||
|
||||
@IBOutlet private var menuIncreaseFontSize: NSMenuItem?
|
||||
@IBOutlet private var menuDecreaseFontSize: NSMenuItem?
|
||||
@@ -577,6 +578,7 @@ class AppDelegate: NSObject,
|
||||
self.menuChangeTabTitle?.setImageIfDesired(systemSymbolName: "pencil.line")
|
||||
self.menuTerminalInspector?.setImageIfDesired(systemSymbolName: "scope")
|
||||
self.menuReadonly?.setImageIfDesired(systemSymbolName: "eye.fill")
|
||||
self.menuSetAsDefaultTerminal?.setImageIfDesired(systemSymbolName: "star.fill")
|
||||
self.menuToggleFullScreen?.setImageIfDesired(systemSymbolName: "square.arrowtriangle.4.outward")
|
||||
self.menuToggleVisibility?.setImageIfDesired(systemSymbolName: "eye")
|
||||
self.menuZoomSplit?.setImageIfDesired(systemSymbolName: "arrow.up.left.and.arrow.down.right")
|
||||
@@ -1292,6 +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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: NSMenuItemValidation
|
||||
@@ -1299,6 +1316,12 @@ extension AppDelegate {
|
||||
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
|
||||
|
||||
case #selector(floatOnTop(_:)),
|
||||
#selector(useAsDefault(_:)):
|
||||
// Float on top items only active if the key window is a primary
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
<outlet property="menuSelectSplitRight" destination="upj-mc-L7X" id="nLY-o1-lky"/>
|
||||
<outlet property="menuSelectionForSearch" destination="TDN-42-Bu7" id="M04-1K-vze"/>
|
||||
<outlet property="menuServices" destination="aQe-vS-j8Q" id="uWQ-Wo-T1L"/>
|
||||
<outlet property="menuSetAsDefaultTerminal" destination="b1t-oB-7MI" id="6Eu-5G-OPo"/>
|
||||
<outlet property="menuSplitDown" destination="UDZ-4y-6xL" id="ptr-mj-Azh"/>
|
||||
<outlet property="menuSplitLeft" destination="Ppv-GP-lQU" id="Xd5-Cd-Jut"/>
|
||||
<outlet property="menuSplitRight" destination="VUR-Ld-nLx" id="RxO-Zw-ovb"/>
|
||||
@@ -109,6 +110,12 @@
|
||||
<action selector="toggleSecureInput:" target="bbz-4X-AYv" id="vWx-z8-5Sy"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Make Ghostty the Default Terminal" id="b1t-oB-7MI" userLabel="Set Ghostty as Default Terminal App">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="setAsDefaultTerminal:" target="bbz-4X-AYv" id="QHh-CA-Qho"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
|
||||
<menuItem title="Services" id="rJe-5J-bwL">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
|
||||
34
macos/Sources/Ghostty/Extensions/NSWorkspace+Ghostty.swift
Normal file
34
macos/Sources/Ghostty/Extensions/NSWorkspace+Ghostty.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -26,4 +26,5 @@ extension NSWorkspace {
|
||||
guard let uti = UTType(filenameExtension: ext) else { return nil}
|
||||
return defaultApplicationURL(forContentType: uti.identifier)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user