macos: new terminal intent
parent
f55c77bc81
commit
7ae5018fe8
|
|
@ -120,6 +120,8 @@
|
||||||
A5E112952AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E112942AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift */; };
|
A5E112952AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E112942AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift */; };
|
||||||
A5E112972AF7401B00C6E0C2 /* ClipboardConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E112962AF7401B00C6E0C2 /* ClipboardConfirmationView.swift */; };
|
A5E112972AF7401B00C6E0C2 /* ClipboardConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E112962AF7401B00C6E0C2 /* ClipboardConfirmationView.swift */; };
|
||||||
A5E4082A2E022E9E0035FEAC /* TabGroupCloseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E408292E022E9B0035FEAC /* TabGroupCloseCoordinator.swift */; };
|
A5E4082A2E022E9E0035FEAC /* TabGroupCloseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E408292E022E9B0035FEAC /* TabGroupCloseCoordinator.swift */; };
|
||||||
|
A5E4082E2E0237460035FEAC /* NewTerminalIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E4082D2E0237410035FEAC /* NewTerminalIntent.swift */; };
|
||||||
|
A5E408302E0271320035FEAC /* GhosttyIntentError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E4082F2E0271320035FEAC /* GhosttyIntentError.swift */; };
|
||||||
A5FEB3002ABB69450068369E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5FEB2FF2ABB69450068369E /* main.swift */; };
|
A5FEB3002ABB69450068369E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5FEB2FF2ABB69450068369E /* main.swift */; };
|
||||||
AEE8B3452B9AA39600260C5E /* NSPasteboard+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */; };
|
AEE8B3452B9AA39600260C5E /* NSPasteboard+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */; };
|
||||||
C159E81D2B66A06B00FDFE9C /* OSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */; };
|
C159E81D2B66A06B00FDFE9C /* OSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */; };
|
||||||
|
|
@ -240,6 +242,8 @@
|
||||||
A5E112942AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipboardConfirmationController.swift; sourceTree = "<group>"; };
|
A5E112942AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipboardConfirmationController.swift; sourceTree = "<group>"; };
|
||||||
A5E112962AF7401B00C6E0C2 /* ClipboardConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipboardConfirmationView.swift; sourceTree = "<group>"; };
|
A5E112962AF7401B00C6E0C2 /* ClipboardConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipboardConfirmationView.swift; sourceTree = "<group>"; };
|
||||||
A5E408292E022E9B0035FEAC /* TabGroupCloseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabGroupCloseCoordinator.swift; sourceTree = "<group>"; };
|
A5E408292E022E9B0035FEAC /* TabGroupCloseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabGroupCloseCoordinator.swift; sourceTree = "<group>"; };
|
||||||
|
A5E4082D2E0237410035FEAC /* NewTerminalIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTerminalIntent.swift; sourceTree = "<group>"; };
|
||||||
|
A5E4082F2E0271320035FEAC /* GhosttyIntentError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhosttyIntentError.swift; sourceTree = "<group>"; };
|
||||||
A5FEB2FF2ABB69450068369E /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
A5FEB2FF2ABB69450068369E /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||||
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSPasteboard+Extension.swift"; sourceTree = "<group>"; };
|
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSPasteboard+Extension.swift"; sourceTree = "<group>"; };
|
||||||
C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OSColor+Extension.swift"; sourceTree = "<group>"; };
|
C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OSColor+Extension.swift"; sourceTree = "<group>"; };
|
||||||
|
|
@ -299,6 +303,7 @@
|
||||||
A56D58872ACDE6BE00508D2C /* Services */,
|
A56D58872ACDE6BE00508D2C /* Services */,
|
||||||
A59630982AEE1C4400D64628 /* Terminal */,
|
A59630982AEE1C4400D64628 /* Terminal */,
|
||||||
A5CBD05A2CA0C5910017A1AE /* QuickTerminal */,
|
A5CBD05A2CA0C5910017A1AE /* QuickTerminal */,
|
||||||
|
A5E4082C2E0237270035FEAC /* App Intents */,
|
||||||
A5E112912AF73E4D00C6E0C2 /* ClipboardConfirmation */,
|
A5E112912AF73E4D00C6E0C2 /* ClipboardConfirmation */,
|
||||||
A57D79252C9C8782001D522E /* Secure Input */,
|
A57D79252C9C8782001D522E /* Secure Input */,
|
||||||
A58636622DEF955100E04A10 /* Splits */,
|
A58636622DEF955100E04A10 /* Splits */,
|
||||||
|
|
@ -598,6 +603,15 @@
|
||||||
path = ClipboardConfirmation;
|
path = ClipboardConfirmation;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
A5E4082C2E0237270035FEAC /* App Intents */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A5E4082D2E0237410035FEAC /* NewTerminalIntent.swift */,
|
||||||
|
A5E4082F2E0271320035FEAC /* GhosttyIntentError.swift */,
|
||||||
|
);
|
||||||
|
path = "App Intents";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
|
@ -777,8 +791,10 @@
|
||||||
A56D58862ACDDB4100508D2C /* Ghostty.Shell.swift in Sources */,
|
A56D58862ACDDB4100508D2C /* Ghostty.Shell.swift in Sources */,
|
||||||
A5985CD72C320C4500C57AD3 /* String+Extension.swift in Sources */,
|
A5985CD72C320C4500C57AD3 /* String+Extension.swift in Sources */,
|
||||||
A5A2A3CC2D444ABB0033CF96 /* NSApplication+Extension.swift in Sources */,
|
A5A2A3CC2D444ABB0033CF96 /* NSApplication+Extension.swift in Sources */,
|
||||||
|
A5E408302E0271320035FEAC /* GhosttyIntentError.swift in Sources */,
|
||||||
A5FEB3002ABB69450068369E /* main.swift in Sources */,
|
A5FEB3002ABB69450068369E /* main.swift in Sources */,
|
||||||
A53A297F2DB4480F00B6E02C /* EventModifiers+Extension.swift in Sources */,
|
A53A297F2DB4480F00B6E02C /* EventModifiers+Extension.swift in Sources */,
|
||||||
|
A5E4082E2E0237460035FEAC /* NewTerminalIntent.swift in Sources */,
|
||||||
A53A297B2DB2E49700B6E02C /* CommandPalette.swift in Sources */,
|
A53A297B2DB2E49700B6E02C /* CommandPalette.swift in Sources */,
|
||||||
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
||||||
A51B78472AF4B58B00F3EDB9 /* TitlebarTabsVenturaTerminalWindow.swift in Sources */,
|
A51B78472AF4B58B00F3EDB9 /* TitlebarTabsVenturaTerminalWindow.swift in Sources */,
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ class AppDelegate: NSObject,
|
||||||
|
|
||||||
// This registers the Ghostty => Services menu to exist.
|
// This registers the Ghostty => Services menu to exist.
|
||||||
NSApp.servicesMenu = menuServices
|
NSApp.servicesMenu = menuServices
|
||||||
|
|
||||||
// Setup a local event monitor for app-level keyboard shortcuts. See
|
// Setup a local event monitor for app-level keyboard shortcuts. See
|
||||||
// localEventHandler for more info why.
|
// localEventHandler for more info why.
|
||||||
_ = NSEvent.addLocalMonitorForEvents(
|
_ = NSEvent.addLocalMonitorForEvents(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
enum GhosttyIntentError: Error, CustomLocalizedStringResourceConvertible {
|
||||||
|
case appUnavailable
|
||||||
|
|
||||||
|
var localizedStringResource: LocalizedStringResource {
|
||||||
|
switch self {
|
||||||
|
case .appUnavailable: return "The Ghostty app isn't properly initialized."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
import AppKit
|
||||||
|
import AppIntents
|
||||||
|
|
||||||
|
/// App intent that allows creating a new terminal window or tab.
|
||||||
|
///
|
||||||
|
/// This requires macOS 15 or greater because we use features of macOS 15 here.
|
||||||
|
@available(macOS 15.0, *)
|
||||||
|
struct NewTerminalIntent: AppIntent {
|
||||||
|
static var title: LocalizedStringResource = "New Terminal"
|
||||||
|
static var description = IntentDescription("Create a new terminal.")
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
title: "Location",
|
||||||
|
description: "The location that the terminal should be created.",
|
||||||
|
default: .window
|
||||||
|
)
|
||||||
|
var location: NewTerminalLocation
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
title: "Working Directory",
|
||||||
|
description: "The working directory to open in the terminal.",
|
||||||
|
supportedContentTypes: [.folder]
|
||||||
|
)
|
||||||
|
var workingDirectory: IntentFile?
|
||||||
|
|
||||||
|
@available(macOS 26.0, *)
|
||||||
|
static var supportedModes: IntentModes = .foreground(.immediate)
|
||||||
|
|
||||||
|
@available(macOS, obsoleted: 26.0, message: "Replaced by supportedModes")
|
||||||
|
static var openAppWhenRun = true
|
||||||
|
|
||||||
|
static var parameterSummary: some ParameterSummary {
|
||||||
|
Summary("New Terminal \(\.$location)")
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func perform() async throws -> some IntentResult {
|
||||||
|
guard let appDelegate = NSApp.delegate as? AppDelegate else {
|
||||||
|
throw GhosttyIntentError.appUnavailable
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = Ghostty.SurfaceConfiguration()
|
||||||
|
|
||||||
|
// If we were given a working directory then open that directory
|
||||||
|
if let url = workingDirectory?.fileURL {
|
||||||
|
let dir = url.hasDirectoryPath ? url : url.deletingLastPathComponent()
|
||||||
|
config.workingDirectory = dir.path(percentEncoded: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch location {
|
||||||
|
case .window:
|
||||||
|
_ = TerminalController.newWindow(
|
||||||
|
appDelegate.ghostty,
|
||||||
|
withBaseConfig: config)
|
||||||
|
|
||||||
|
case .tab:
|
||||||
|
_ = TerminalController.newTab(
|
||||||
|
appDelegate.ghostty,
|
||||||
|
from: TerminalController.preferredParent?.window,
|
||||||
|
withBaseConfig: config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .result()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: NewTerminalLocation
|
||||||
|
|
||||||
|
enum NewTerminalLocation: String {
|
||||||
|
case tab
|
||||||
|
case window
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NewTerminalLocation: AppEnum {
|
||||||
|
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Terminal Location")
|
||||||
|
|
||||||
|
static var caseDisplayRepresentations: [Self: DisplayRepresentation] = [
|
||||||
|
.tab: .init(title: "Tab"),
|
||||||
|
.window: .init(title: "Window"),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -169,7 +169,7 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
||||||
private static var lastCascadePoint = NSPoint(x: 0, y: 0)
|
private static var lastCascadePoint = NSPoint(x: 0, y: 0)
|
||||||
|
|
||||||
// The preferred parent terminal controller.
|
// The preferred parent terminal controller.
|
||||||
private static var preferredParent: TerminalController? {
|
static var preferredParent: TerminalController? {
|
||||||
all.first {
|
all.first {
|
||||||
$0.window?.isMainWindow ?? false
|
$0.window?.isMainWindow ?? false
|
||||||
} ?? all.last
|
} ?? all.last
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue