macos: new terminal intent

This commit is contained in:
Mitchell Hashimoto
2025-06-17 20:59:12 -07:00
parent f55c77bc81
commit 7ae5018fe8
5 changed files with 108 additions and 2 deletions

View File

@@ -167,7 +167,7 @@ class AppDelegate: NSObject,
// This registers the Ghostty => Services menu to exist.
NSApp.servicesMenu = menuServices
// Setup a local event monitor for app-level keyboard shortcuts. See
// localEventHandler for more info why.
_ = NSEvent.addLocalMonitorForEvents(

View File

@@ -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."
}
}
}

View File

@@ -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"),
]
}

View File

@@ -169,7 +169,7 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
private static var lastCascadePoint = NSPoint(x: 0, y: 0)
// The preferred parent terminal controller.
private static var preferredParent: TerminalController? {
static var preferredParent: TerminalController? {
all.first {
$0.window?.isMainWindow ?? false
} ?? all.last