2025-06-21 06:39:19 -07:00

182 lines
4.8 KiB
Swift

import AppKit
import AppIntents
/// App intent to input text in a terminal.
struct InputTextIntent: AppIntent {
static var title: LocalizedStringResource = "Input Text to Terminal"
@Parameter(
title: "Text",
description: "The text to input to the terminal. The text will be inputted as if it was pasted.",
inputOptions: String.IntentInputOptions(
capitalizationType: .none,
multiline: true,
autocorrect: false,
smartQuotes: false,
smartDashes: false
)
)
var text: String
@Parameter(
title: "Terminal",
description: "The terminal to scope this action to."
)
var terminal: TerminalEntity
@available(macOS 26.0, *)
static var supportedModes: IntentModes = [.background, .foreground]
@MainActor
func perform() async throws -> some IntentResult {
guard let surface = terminal.surfaceModel else {
throw GhosttyIntentError.surfaceNotFound
}
surface.sendText(text)
return .result()
}
}
/// App intent to trigger a keyboard event.
struct KeyEventIntent: AppIntent {
static var title: LocalizedStringResource = "Send Keyboard Event to Terminal"
static var description = IntentDescription("Simulate a keyboard event. This will not handle text encoding; use the 'Input Text' action for that.")
@Parameter(
title: "Key",
description: "The key to send to the terminal.",
default: .enter
)
var key: Ghostty.Input.Key
@Parameter(
title: "Modifier(s)",
description: "The modifiers to send with the key event.",
default: []
)
var mods: [KeyEventMods]
@Parameter(
title: "Event Type",
description: "A key press or release.",
default: .press
)
var action: Ghostty.Input.Action
@Parameter(
title: "Terminal",
description: "The terminal to scope this action to."
)
var terminal: TerminalEntity
@available(macOS 26.0, *)
static var supportedModes: IntentModes = [.background, .foreground]
@MainActor
func perform() async throws -> some IntentResult {
guard let surface = terminal.surfaceModel else {
throw GhosttyIntentError.surfaceNotFound
}
// Convert KeyEventMods array to Ghostty.Input.Mods
let ghosttyMods = mods.reduce(Ghostty.Input.Mods()) { result, mod in
result.union(mod.ghosttyMod)
}
let keyEvent = Ghostty.Input.KeyEvent(
key: key,
action: action,
mods: ghosttyMods
)
surface.sendKeyEvent(keyEvent)
return .result()
}
}
// MARK: MouseButtonIntent
/// App intent to trigger a mouse button event.
struct MouseButtonIntent: AppIntent {
static var title: LocalizedStringResource = "Send Mouse Button Event to Terminal"
@Parameter(
title: "Button",
description: "The mouse button to press or release.",
default: .left
)
var button: Ghostty.Input.MouseButton
@Parameter(
title: "Action",
description: "Whether to press or release the button.",
default: .press
)
var action: Ghostty.Input.MouseState
@Parameter(
title: "Modifier(s)",
description: "The modifiers to send with the mouse event.",
default: []
)
var mods: [KeyEventMods]
@Parameter(
title: "Terminal",
description: "The terminal to scope this action to."
)
var terminal: TerminalEntity
@available(macOS 26.0, *)
static var supportedModes: IntentModes = [.background, .foreground]
@MainActor
func perform() async throws -> some IntentResult {
guard let surface = terminal.surfaceModel else {
throw GhosttyIntentError.surfaceNotFound
}
// Convert KeyEventMods array to Ghostty.Input.Mods
let ghosttyMods = mods.reduce(Ghostty.Input.Mods()) { result, mod in
result.union(mod.ghosttyMod)
}
let mouseEvent = Ghostty.Input.MouseButtonEvent(
action: action,
button: button,
mods: ghosttyMods
)
surface.sendMouseButton(mouseEvent)
return .result()
}
}
// MARK: Mods
enum KeyEventMods: String, AppEnum, CaseIterable {
case shift
case control
case option
case command
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Modifier Key")
static var caseDisplayRepresentations: [KeyEventMods : DisplayRepresentation] = [
.shift: "Shift",
.control: "Control",
.option: "Option",
.command: "Command"
]
var ghosttyMod: Ghostty.Input.Mods {
switch self {
case .shift: .shift
case .control: .ctrl
case .option: .alt
case .command: .super
}
}
}