macos: add terminals element to window and tab AppleScript classes

Expose terminal surfaces as elements on both ScriptWindow and ScriptTab,
allowing AppleScript to enumerate terminals scoped to a specific window
or tab (e.g. `terminals of window 1`, `terminals of tab 1 of window 1`).

Changes:
- Add `<element type="terminal">` to window and tab classes in Ghostty.sdef
- Add `terminals` computed property and `valueInTerminalsWithUniqueID:`
  lookup to ScriptWindow (returns all surfaces across all tabs)
- Add `terminals` computed property and `valueInTerminalsWithUniqueID:`
  lookup to ScriptTab (returns surfaces within that tab)
This commit is contained in:
Mitchell Hashimoto
2026-03-06 08:06:37 -08:00
parent f72d41675b
commit e514035519
3 changed files with 44 additions and 0 deletions

View File

@@ -35,6 +35,9 @@
<element type="tab" access="r">
<cocoa key="tabs"/>
</element>
<element type="terminal" access="r">
<cocoa key="terminals"/>
</element>
</class>
<class name="tab" code="Gtab" plural="tabs" description="A tab within a Ghostty window.">
@@ -42,6 +45,9 @@
<property name="id" code="ID " type="text" access="r" description="Stable ID for this tab."/>
<property name="index" code="pidx" type="integer" access="r" description="1-based index of this tab in its window."/>
<property name="selected" code="GTsl" type="boolean" access="r" description="Whether this tab is selected in its window."/>
<element type="terminal" access="r">
<cocoa key="terminals"/>
</element>
</class>
<class name="terminal" code="Gtrm" plural="terminals" description="An individual terminal surface.">

View File

@@ -55,6 +55,25 @@ final class ScriptTab: NSObject {
return window?.tabIsSelected(controller) ?? false
}
/// Exposed as the AppleScript `terminals` element on a tab.
///
/// Returns all terminal surfaces (split panes) within this tab.
@objc(terminals)
var terminals: [ScriptTerminal] {
guard let controller else { return [] }
return (controller.surfaceTree.root?.leaves() ?? [])
.map(ScriptTerminal.init)
}
/// Enables unique-ID lookup for `terminals` references on a tab.
@objc(valueInTerminalsWithUniqueID:)
func valueInTerminals(uniqueID: String) -> ScriptTerminal? {
guard let controller else { return nil }
return (controller.surfaceTree.root?.leaves() ?? [])
.first(where: { $0.id.uuidString == uniqueID })
.map(ScriptTerminal.init)
}
/// Provides Cocoa scripting with a canonical "path" back to this object.
override var objectSpecifier: NSScriptObjectSpecifier? {
guard let window else { return nil }

View File

@@ -73,6 +73,25 @@ final class ScriptWindow: NSObject {
return ScriptTab(window: self, controller: controller)
}
/// Exposed as the AppleScript `terminals` element on a window.
///
/// Returns all terminal surfaces across every tab in this window.
@objc(terminals)
var terminals: [ScriptTerminal] {
controllers
.flatMap { $0.surfaceTree.root?.leaves() ?? [] }
.map(ScriptTerminal.init)
}
/// Enables unique-ID lookup for `terminals` references on a window.
@objc(valueInTerminalsWithUniqueID:)
func valueInTerminals(uniqueID: String) -> ScriptTerminal? {
controllers
.flatMap { $0.surfaceTree.root?.leaves() ?? [] }
.first(where: { $0.id.uuidString == uniqueID })
.map(ScriptTerminal.init)
}
/// AppleScript tab indexes are 1-based, so we add one to Swift's 0-based
/// array index.
func tabIndex(for controller: BaseTerminalController) -> Int? {