import AppKit import AppIntents import GhosttyKit /// 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: "Command", description: "Command to execute instead of the default shell." ) var command: String? @Parameter( title: "Working Directory", description: "The working directory to open in the terminal.", supportedContentTypes: [.folder] ) var workingDirectory: IntentFile? @Parameter( title: "Environment Variables", description: "Environment variables in `KEY=VALUE` format.", default: [] ) var env: [String] @Parameter( title: "Parent Terminal", description: "The terminal to inherit the base configuration from." ) var parent: TerminalEntity? @available(macOS 26.0, *) static var supportedModes: IntentModes = .foreground(.immediate) @available(macOS, obsoleted: 26.0, message: "Replaced by supportedModes") static var openAppWhenRun = true @MainActor func perform() async throws -> some IntentResult & ReturnsValue { guard await requestIntentPermission() else { throw GhosttyIntentError.permissionDenied } guard let appDelegate = NSApp.delegate as? AppDelegate else { throw GhosttyIntentError.appUnavailable } let ghostty = appDelegate.ghostty var config = Ghostty.SurfaceConfiguration() config.command = command // 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) } // Parse environment variables from KEY=VALUE format for envVar in env { if let separatorIndex = envVar.firstIndex(of: "=") { let key = String(envVar[...NewDirection? { switch self { case .splitLeft: return .left case .splitRight: return .right case .splitUp: return .up case .splitDown: return .down default: return nil } } } extension NewTerminalLocation: AppEnum { static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Terminal Location") static var caseDisplayRepresentations: [Self: DisplayRepresentation] = [ .tab: .init(title: "Tab"), .window: .init(title: "Window"), .splitLeft: .init(title: "Split Left"), .splitRight: .init(title: "Split Right"), .splitUp: .init(title: "Split Up"), .splitDown: .init(title: "Split Down"), ] }