diff --git a/macos/Sources/Features/App Intents/FocusTerminalIntent.swift b/macos/Sources/Features/App Intents/FocusTerminalIntent.swift new file mode 100644 index 000000000..4e813e842 --- /dev/null +++ b/macos/Sources/Features/App Intents/FocusTerminalIntent.swift @@ -0,0 +1,35 @@ +import AppKit +import AppIntents +import GhosttyKit + +struct FocusTerminalIntent: AppIntent { + static var title: LocalizedStringResource = "Focus Terminal" + static var description = IntentDescription("Move focus to an existing terminal.") + + @Parameter( + title: "Terminal", + description: "The terminal to focus.", + ) + var terminal: TerminalEntity + + @available(macOS 26.0, *) + static var supportedModes: IntentModes = .background + + @MainActor + func perform() async throws -> some IntentResult { + guard await requestIntentPermission() else { + throw GhosttyIntentError.permissionDenied + } + + guard let surfaceView = terminal.surfaceView else { + throw GhosttyIntentError.surfaceNotFound + } + + guard let controller = surfaceView.window?.windowController as? BaseTerminalController else { + return .result() + } + + controller.focusSurface(surfaceView) + return .result() + } +} diff --git a/macos/Sources/Features/Terminal/BaseTerminalController.swift b/macos/Sources/Features/Terminal/BaseTerminalController.swift index 2de967daf..abf7d4a61 100644 --- a/macos/Sources/Features/Terminal/BaseTerminalController.swift +++ b/macos/Sources/Features/Terminal/BaseTerminalController.swift @@ -233,6 +233,16 @@ class BaseTerminalController: NSWindowController, return newView } + func focusSurface(_ view: Ghostty.SurfaceView) { + guard surfaceTree.contains(view) else { return } + + DispatchQueue.main.async { + Ghostty.moveFocus(to: view, from: self.focusedSurface) + view.window?.makeKeyAndOrderFront(nil) + NSApp.activate(ignoringOtherApps: true) + } + } + /// Called when the surfaceTree variable changed. /// /// Subclasses should call super first.