chore: import upstream snapshot with attribution
Test / build-libghostty-vt (x86_64-windows-gnu) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-ios) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt-macos (x86_64-macos) (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-dist (push) Has been cancelled
Test / GTK x11=true wayland=true (push) Has been cancelled
Test / Build -Dsimd=false (push) Has been cancelled
Test / Build -Dsimd=true (push) Has been cancelled
Test / Build -Dsentry=false (push) Has been cancelled
Test / Build -Dsentry=true (push) Has been cancelled
Test / zig-fmt (push) Has been cancelled
Test / GitHub Actions Pins (push) Has been cancelled
Test / prettier (push) Has been cancelled
Test / swiftlint (push) Has been cancelled
Test / alejandra (push) Has been cancelled
Test / typos (push) Has been cancelled
Nix / Required Checks: Nix (push) Has been cancelled
Nix / check-zig-cache-hash (push) Has been cancelled
Test / skip (push) Has been cancelled
Test / Required Checks: Test (push) Has been cancelled
Test / build-bench (push) Has been cancelled
Test / list-examples (push) Has been cancelled
Test / Example ${{ matrix.dir }} (Windows) (push) Has been cancelled
Test / build-cmake (push) Has been cancelled
Test / test-lib-vt-pkgconfig (push) Has been cancelled
Test / build-flatpak (push) Has been cancelled
Test / build-snap (push) Has been cancelled
Test / build-libghostty-vt (aarch64-linux) (push) Has been cancelled
Test / build-libghostty-vt (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt (wasm32-freestanding) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux-musl) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-macos) (push) Has been cancelled
Test / build-libghostty-vt-android (aarch64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-android (arm-linux-androideabi) (push) Has been cancelled
Test / build-libghostty-vt-android (x86_64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-windows (push) Has been cancelled
Test / build-libghostty-windows-gnu (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-linux-libghostty (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-dist-lib-vt (push) Has been cancelled
Test / trigger-snap (push) Has been cancelled
Test / trigger-flatpak (push) Has been cancelled
Test / build-macos (push) Has been cancelled
Test / build-macos-freetype (push) Has been cancelled
Test / test (push) Has been cancelled
Test / test-lib-vt (push) Has been cancelled
Test / GTK x11=false wayland=false (push) Has been cancelled
Test / GTK x11=true wayland=false (push) Has been cancelled
Test / GTK x11=false wayland=true (push) Has been cancelled
Test / test-macos (push) Has been cancelled
Test / test-windows (push) Has been cancelled
Test / Build -Di18n=false (push) Has been cancelled
Test / Build -Di18n=true (push) Has been cancelled
Test / Build test/fuzz-libghostty (push) Has been cancelled
Test / shellcheck (push) Has been cancelled
Test / translations (push) Has been cancelled
Test / blueprint-compiler (push) Has been cancelled
Test / Test pkg/wuffs (push) Has been cancelled
Test / Test build on Debian 13 (push) Has been cancelled
Test / valgrind (push) Has been cancelled
Test / Example ${{ matrix.dir }} (push) Has been cancelled
Test / build-libghostty-vt (x86_64-windows-gnu) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-ios) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt-macos (x86_64-macos) (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-dist (push) Has been cancelled
Test / GTK x11=true wayland=true (push) Has been cancelled
Test / Build -Dsimd=false (push) Has been cancelled
Test / Build -Dsimd=true (push) Has been cancelled
Test / Build -Dsentry=false (push) Has been cancelled
Test / Build -Dsentry=true (push) Has been cancelled
Test / zig-fmt (push) Has been cancelled
Test / GitHub Actions Pins (push) Has been cancelled
Test / prettier (push) Has been cancelled
Test / swiftlint (push) Has been cancelled
Test / alejandra (push) Has been cancelled
Test / typos (push) Has been cancelled
Nix / Required Checks: Nix (push) Has been cancelled
Nix / check-zig-cache-hash (push) Has been cancelled
Test / skip (push) Has been cancelled
Test / Required Checks: Test (push) Has been cancelled
Test / build-bench (push) Has been cancelled
Test / list-examples (push) Has been cancelled
Test / Example ${{ matrix.dir }} (Windows) (push) Has been cancelled
Test / build-cmake (push) Has been cancelled
Test / test-lib-vt-pkgconfig (push) Has been cancelled
Test / build-flatpak (push) Has been cancelled
Test / build-snap (push) Has been cancelled
Test / build-libghostty-vt (aarch64-linux) (push) Has been cancelled
Test / build-libghostty-vt (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt (wasm32-freestanding) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux-musl) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-macos) (push) Has been cancelled
Test / build-libghostty-vt-android (aarch64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-android (arm-linux-androideabi) (push) Has been cancelled
Test / build-libghostty-vt-android (x86_64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-windows (push) Has been cancelled
Test / build-libghostty-windows-gnu (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-linux-libghostty (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-dist-lib-vt (push) Has been cancelled
Test / trigger-snap (push) Has been cancelled
Test / trigger-flatpak (push) Has been cancelled
Test / build-macos (push) Has been cancelled
Test / build-macos-freetype (push) Has been cancelled
Test / test (push) Has been cancelled
Test / test-lib-vt (push) Has been cancelled
Test / GTK x11=false wayland=false (push) Has been cancelled
Test / GTK x11=true wayland=false (push) Has been cancelled
Test / GTK x11=false wayland=true (push) Has been cancelled
Test / test-macos (push) Has been cancelled
Test / test-windows (push) Has been cancelled
Test / Build -Di18n=false (push) Has been cancelled
Test / Build -Di18n=true (push) Has been cancelled
Test / Build test/fuzz-libghostty (push) Has been cancelled
Test / shellcheck (push) Has been cancelled
Test / translations (push) Has been cancelled
Test / blueprint-compiler (push) Has been cancelled
Test / Test pkg/wuffs (push) Has been cancelled
Test / Test build on Debian 13 (push) Has been cancelled
Test / valgrind (push) Has been cancelled
Test / Example ${{ matrix.dir }} (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
import AppKit
|
||||
|
||||
/// AppleScript-facing wrapper around a single tab in a scripting window.
|
||||
///
|
||||
/// `ScriptWindow.tabs` vends these objects so AppleScript can traverse
|
||||
/// `window -> tab` without knowing anything about AppKit controllers.
|
||||
@MainActor
|
||||
@objc(GhosttyScriptTab)
|
||||
final class ScriptTab: NSObject {
|
||||
/// Stable identifier used by AppleScript `tab id "..."` references.
|
||||
private let stableID: String
|
||||
|
||||
/// Weak back-reference to the scripting window that owns this tab wrapper.
|
||||
///
|
||||
/// We only need this for dynamic properties (`index`, `selected`) and for
|
||||
/// building an object specifier path.
|
||||
private weak var window: ScriptWindow?
|
||||
|
||||
/// Live terminal controller for this tab.
|
||||
///
|
||||
/// This can become `nil` if the tab closes while a script is running.
|
||||
private weak var controller: BaseTerminalController?
|
||||
|
||||
/// Called by `ScriptWindow.tabs` / `ScriptWindow.selectedTab`.
|
||||
///
|
||||
/// The ID is computed once so object specifiers built from this instance keep
|
||||
/// a consistent tab identity.
|
||||
init(window: ScriptWindow, controller: BaseTerminalController) {
|
||||
self.stableID = Self.stableID(controller: controller)
|
||||
self.window = window
|
||||
self.controller = controller
|
||||
}
|
||||
|
||||
/// Exposed as the AppleScript `id` property.
|
||||
@objc(id)
|
||||
var idValue: String {
|
||||
guard NSApp.isAppleScriptEnabled else { return "" }
|
||||
return stableID
|
||||
}
|
||||
|
||||
/// Exposed as the AppleScript `title` property.
|
||||
///
|
||||
/// Returns the title of the tab's window.
|
||||
@objc(title)
|
||||
var title: String {
|
||||
guard NSApp.isAppleScriptEnabled else { return "" }
|
||||
return controller?.window?.title ?? ""
|
||||
}
|
||||
|
||||
/// Exposed as the AppleScript `index` property.
|
||||
///
|
||||
/// Cocoa scripting expects this to be 1-based for user-facing collections.
|
||||
@objc(index)
|
||||
var index: Int {
|
||||
guard NSApp.isAppleScriptEnabled else { return 0 }
|
||||
guard let controller else { return 0 }
|
||||
return window?.tabIndex(for: controller) ?? 0
|
||||
}
|
||||
|
||||
/// Exposed as the AppleScript `selected` property.
|
||||
///
|
||||
/// Powers script conditions such as `if selected of tab 1 then ...`.
|
||||
@objc(selected)
|
||||
var selected: Bool {
|
||||
guard NSApp.isAppleScriptEnabled else { return false }
|
||||
guard let controller else { return false }
|
||||
return window?.tabIsSelected(controller) ?? false
|
||||
}
|
||||
|
||||
/// Exposed as the AppleScript `focused terminal` property.
|
||||
///
|
||||
/// Uses the currently focused surface for this tab.
|
||||
@objc(focusedTerminal)
|
||||
var focusedTerminal: ScriptTerminal? {
|
||||
guard NSApp.isAppleScriptEnabled else { return nil }
|
||||
guard let controller else { return nil }
|
||||
guard let surface = controller.focusedSurface,
|
||||
controller.surfaceTree.contains(surface)
|
||||
else { return nil }
|
||||
|
||||
return ScriptTerminal(surfaceView: surface)
|
||||
}
|
||||
|
||||
/// Best-effort native window containing this tab.
|
||||
var parentWindow: NSWindow? {
|
||||
guard NSApp.isAppleScriptEnabled else { return nil }
|
||||
return controller?.window
|
||||
}
|
||||
|
||||
/// Live controller backing this tab wrapper.
|
||||
var parentController: BaseTerminalController? {
|
||||
guard NSApp.isAppleScriptEnabled else { return nil }
|
||||
return controller
|
||||
}
|
||||
|
||||
/// Exposed as the AppleScript `terminals` element on a tab.
|
||||
///
|
||||
/// Returns all terminal surfaces (split panes) within this tab.
|
||||
@objc(terminals)
|
||||
var terminals: [ScriptTerminal] {
|
||||
guard NSApp.isAppleScriptEnabled else { return [] }
|
||||
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 NSApp.isAppleScriptEnabled else { return nil }
|
||||
guard let controller else { return nil }
|
||||
return (controller.surfaceTree.root?.leaves() ?? [])
|
||||
.first(where: { $0.id.uuidString == uniqueID })
|
||||
.map(ScriptTerminal.init)
|
||||
}
|
||||
|
||||
/// Handler for `select tab <tab>`.
|
||||
@objc(handleSelectTabCommand:)
|
||||
func handleSelectTab(_ command: NSScriptCommand) -> Any? {
|
||||
guard NSApp.validateScript(command: command) else { return nil }
|
||||
|
||||
guard let tabContainerWindow = parentWindow else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = "Tab is no longer available."
|
||||
return nil
|
||||
}
|
||||
|
||||
tabContainerWindow.makeKeyAndOrderFront(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Handler for `close tab <tab>`.
|
||||
@objc(handleCloseTabCommand:)
|
||||
func handleCloseTab(_ command: NSScriptCommand) -> Any? {
|
||||
guard NSApp.validateScript(command: command) else { return nil }
|
||||
|
||||
guard let tabController = parentController else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = "Tab is no longer available."
|
||||
return nil
|
||||
}
|
||||
|
||||
if let managedTerminalController = tabController as? TerminalController {
|
||||
managedTerminalController.closeTabImmediately(registerRedo: false)
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let tabContainerWindow = parentWindow else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = "Tab container window is no longer available."
|
||||
return nil
|
||||
}
|
||||
|
||||
tabContainerWindow.close()
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Provides Cocoa scripting with a canonical "path" back to this object.
|
||||
override var objectSpecifier: NSScriptObjectSpecifier? {
|
||||
guard NSApp.isAppleScriptEnabled else { return nil }
|
||||
guard let window else { return nil }
|
||||
guard let windowClassDescription = window.classDescription as? NSScriptClassDescription else {
|
||||
return nil
|
||||
}
|
||||
guard let windowSpecifier = window.objectSpecifier else { return nil }
|
||||
|
||||
// This tells Cocoa how to re-find this tab later:
|
||||
// application -> scriptWindows[id] -> tabs[id].
|
||||
return NSUniqueIDSpecifier(
|
||||
containerClassDescription: windowClassDescription,
|
||||
containerSpecifier: windowSpecifier,
|
||||
key: "tabs",
|
||||
uniqueID: stableID
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension ScriptTab {
|
||||
/// Stable ID for one tab controller.
|
||||
///
|
||||
/// Tab identity belongs to `ScriptTab`, so both tab creation and tab ID
|
||||
/// lookups in `ScriptWindow` call this helper.
|
||||
static func stableID(controller: BaseTerminalController) -> String {
|
||||
"tab-\(ObjectIdentifier(controller).hexString)"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user