mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-18 05:20:29 +00:00
macos: add new tab command
This commit is contained in:
@@ -16,6 +16,9 @@
|
||||
<responds-to command="new window">
|
||||
<cocoa method="handleNewWindowScriptCommand:"/>
|
||||
</responds-to>
|
||||
<responds-to command="new tab">
|
||||
<cocoa method="handleNewTabScriptCommand:"/>
|
||||
</responds-to>
|
||||
<responds-to command="new surface configuration">
|
||||
<cocoa method="handleNewSurfaceConfigurationScriptCommand:"/>
|
||||
</responds-to>
|
||||
@@ -141,6 +144,16 @@
|
||||
<result type="window" description="The newly created window."/>
|
||||
</command>
|
||||
|
||||
<command name="new tab" code="GhstNTab" description="Create a new Ghostty tab.">
|
||||
<parameter name="in" code="GNtW" type="window" optional="yes" description="Target window for the new tab.">
|
||||
<cocoa key="window"/>
|
||||
</parameter>
|
||||
<parameter name="with configuration" code="GNtS" type="surface configuration" optional="yes" description="Base surface configuration for the initial terminal.">
|
||||
<cocoa key="configuration"/>
|
||||
</parameter>
|
||||
<result type="tab" description="The newly created tab."/>
|
||||
</command>
|
||||
|
||||
<command name="split" code="GhstSplt" description="Split a terminal in the given direction.">
|
||||
<cocoa class="GhosttyScriptSplitCommand"/>
|
||||
<parameter name="terminal" code="GSpT" type="terminal" description="The terminal to split.">
|
||||
|
||||
@@ -182,6 +182,80 @@ extension NSApplication {
|
||||
// has not refreshed yet in the current run loop.
|
||||
return ScriptWindow(primaryController: controller)
|
||||
}
|
||||
|
||||
/// Handler for the `new tab` AppleScript command.
|
||||
///
|
||||
/// Required selector name from the command in `sdef`:
|
||||
/// `handleNewTabScriptCommand:`.
|
||||
///
|
||||
/// Accepts an optional target window and optional surface configuration.
|
||||
/// If no window is provided, this mirrors App Intents and uses the
|
||||
/// preferred parent window.
|
||||
///
|
||||
/// Returns the newly created scripting tab object.
|
||||
@objc(handleNewTabScriptCommand:)
|
||||
func handleNewTabScriptCommand(_ command: NSScriptCommand) -> Any? {
|
||||
guard let appDelegate = delegate as? AppDelegate else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = "Ghostty app delegate is unavailable."
|
||||
return nil
|
||||
}
|
||||
|
||||
let baseConfig: Ghostty.SurfaceConfiguration
|
||||
do {
|
||||
if let scriptRecord = command.evaluatedArguments?["configuration"] as? NSDictionary {
|
||||
baseConfig = try Ghostty.SurfaceConfiguration(scriptRecord: scriptRecord)
|
||||
} else {
|
||||
baseConfig = Ghostty.SurfaceConfiguration()
|
||||
}
|
||||
} catch {
|
||||
command.scriptErrorNumber = errAECoercionFail
|
||||
command.scriptErrorString = error.localizedDescription
|
||||
return nil
|
||||
}
|
||||
|
||||
let targetWindow = command.evaluatedArguments?["window"] as? ScriptWindow
|
||||
let parentWindow: NSWindow?
|
||||
if let targetWindow {
|
||||
guard let resolvedWindow = targetWindow.preferredParentWindow else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = "Target window is no longer available."
|
||||
return nil
|
||||
}
|
||||
|
||||
parentWindow = resolvedWindow
|
||||
} else {
|
||||
parentWindow = TerminalController.preferredParent?.window
|
||||
}
|
||||
|
||||
guard let createdController = TerminalController.newTab(
|
||||
appDelegate.ghostty,
|
||||
from: parentWindow,
|
||||
withBaseConfig: baseConfig
|
||||
) else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = "Failed to create tab."
|
||||
return nil
|
||||
}
|
||||
|
||||
let createdTabID = ScriptTab.stableID(controller: createdController)
|
||||
|
||||
if let targetWindow,
|
||||
let scriptTab = targetWindow.valueInTabs(uniqueID: createdTabID) {
|
||||
return scriptTab
|
||||
}
|
||||
|
||||
for scriptWindow in scriptWindows {
|
||||
if let scriptTab = scriptWindow.valueInTabs(uniqueID: createdTabID) {
|
||||
return scriptTab
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to wrapping the created controller if AppKit tab-group
|
||||
// bookkeeping has not fully refreshed in the current run loop.
|
||||
let fallbackWindow = ScriptWindow(primaryController: createdController)
|
||||
return ScriptTab(window: fallbackWindow, controller: createdController)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private Helpers
|
||||
|
||||
@@ -9,6 +9,10 @@ extension Ghostty.SurfaceConfiguration: ScriptRecord {
|
||||
init(scriptRecord source: NSDictionary?) throws {
|
||||
self.init()
|
||||
|
||||
guard let source else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let raw = source as? [String: Any] else {
|
||||
throw RecordParseError.invalidType(parameter: "configuration", expected: "a surface configuration record")
|
||||
}
|
||||
|
||||
@@ -111,6 +111,11 @@ final class ScriptWindow: NSObject {
|
||||
selectedController === controller
|
||||
}
|
||||
|
||||
/// Best-effort native window to use as a tab parent for AppleScript commands.
|
||||
var preferredParentWindow: NSWindow? {
|
||||
selectedController?.window ?? controllers.first?.window
|
||||
}
|
||||
|
||||
/// Resolves a previously generated tab ID back to a live controller.
|
||||
private func controller(tabID: String) -> BaseTerminalController? {
|
||||
controllers.first(where: { ScriptTab.stableID(controller: $0) == tabID })
|
||||
|
||||
Reference in New Issue
Block a user