Update ghostty to v1.3.0 (#1142)
* Update ghostty to v1.3.0 * Add bell handling and AppleScript support * Add zsh shell integration handoff test * Fix Ghostty zsh integration handoff in cmux * Add terminal keypress notification dismissal test * Dismiss terminal notifications on keypress * Address PR review feedback * Tighten notification dismissal regression test * Pin GhosttyKit checksum for latest ghostty
This commit is contained in:
parent
55b619b538
commit
dea60ea71c
14 changed files with 1542 additions and 9 deletions
705
Sources/AppleScriptSupport.swift
Normal file
705
Sources/AppleScriptSupport.swift
Normal file
|
|
@ -0,0 +1,705 @@
|
|||
import AppKit
|
||||
|
||||
private enum AppleScriptStrings {
|
||||
static let disabled = String(
|
||||
localized: "applescript.error.disabled",
|
||||
defaultValue: "AppleScript is disabled by the macos-applescript configuration."
|
||||
)
|
||||
static let missingAction = String(
|
||||
localized: "applescript.error.missingAction",
|
||||
defaultValue: "Missing action string."
|
||||
)
|
||||
static let missingInputText = String(
|
||||
localized: "applescript.error.missingInputText",
|
||||
defaultValue: "Missing input text."
|
||||
)
|
||||
static let missingTerminalTarget = String(
|
||||
localized: "applescript.error.missingTerminalTarget",
|
||||
defaultValue: "Missing terminal target."
|
||||
)
|
||||
static let missingSplitDirection = String(
|
||||
localized: "applescript.error.missingSplitDirection",
|
||||
defaultValue: "Missing or unknown split direction."
|
||||
)
|
||||
static let windowUnavailable = String(
|
||||
localized: "applescript.error.windowUnavailable",
|
||||
defaultValue: "Window is no longer available."
|
||||
)
|
||||
static let workspaceUnavailable = String(
|
||||
localized: "applescript.error.workspaceUnavailable",
|
||||
defaultValue: "Workspace is no longer available."
|
||||
)
|
||||
static let terminalUnavailable = String(
|
||||
localized: "applescript.error.terminalUnavailable",
|
||||
defaultValue: "Terminal is no longer available."
|
||||
)
|
||||
static let failedToCreateWindow = String(
|
||||
localized: "applescript.error.failedToCreateWindow",
|
||||
defaultValue: "Failed to create window."
|
||||
)
|
||||
static let failedToCreateWorkspace = String(
|
||||
localized: "applescript.error.failedToCreateWorkspace",
|
||||
defaultValue: "Failed to create workspace."
|
||||
)
|
||||
static let failedToCreateSplit = String(
|
||||
localized: "applescript.error.failedToCreateSplit",
|
||||
defaultValue: "Failed to create split."
|
||||
)
|
||||
}
|
||||
|
||||
private extension String {
|
||||
var fourCharCode: UInt32 {
|
||||
utf8.reduce(0) { ($0 << 8) + UInt32($1) }
|
||||
}
|
||||
}
|
||||
|
||||
private extension Workspace {
|
||||
func scriptingTerminalPanels() -> [TerminalPanel] {
|
||||
var results: [TerminalPanel] = []
|
||||
var seen: Set<UUID> = []
|
||||
|
||||
for panelId in sidebarOrderedPanelIds() {
|
||||
guard seen.insert(panelId).inserted,
|
||||
let terminal = terminalPanel(for: panelId) else {
|
||||
continue
|
||||
}
|
||||
results.append(terminal)
|
||||
}
|
||||
|
||||
let remaining = panels.values
|
||||
.compactMap { $0 as? TerminalPanel }
|
||||
.sorted { $0.id.uuidString < $1.id.uuidString }
|
||||
|
||||
for terminal in remaining where seen.insert(terminal.id).inserted {
|
||||
results.append(terminal)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
extension NSApplication {
|
||||
var isAppleScriptEnabled: Bool {
|
||||
GhosttyApp.shared.appleScriptAutomationEnabled()
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func validateScript(command: NSScriptCommand) -> Bool {
|
||||
guard isAppleScriptEnabled else {
|
||||
command.scriptErrorNumber = errAEEventNotPermitted
|
||||
command.scriptErrorString = AppleScriptStrings.disabled
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@objc(scriptWindows)
|
||||
var scriptWindows: [ScriptWindow] {
|
||||
guard isAppleScriptEnabled,
|
||||
let appDelegate = AppDelegate.shared else {
|
||||
return []
|
||||
}
|
||||
return appDelegate.scriptableMainWindows().map { ScriptWindow(windowId: $0.windowId) }
|
||||
}
|
||||
|
||||
@objc(frontWindow)
|
||||
var frontWindow: ScriptWindow? {
|
||||
scriptWindows.first
|
||||
}
|
||||
|
||||
@objc(valueInScriptWindowsWithUniqueID:)
|
||||
func valueInScriptWindows(uniqueID: String) -> ScriptWindow? {
|
||||
guard isAppleScriptEnabled,
|
||||
let windowId = UUID(uuidString: uniqueID),
|
||||
let appDelegate = AppDelegate.shared,
|
||||
appDelegate.scriptableMainWindow(windowId: windowId) != nil else {
|
||||
return nil
|
||||
}
|
||||
return ScriptWindow(windowId: windowId)
|
||||
}
|
||||
|
||||
@objc(terminals)
|
||||
var terminals: [ScriptTerminal] {
|
||||
guard isAppleScriptEnabled,
|
||||
let appDelegate = AppDelegate.shared else {
|
||||
return []
|
||||
}
|
||||
|
||||
return appDelegate.scriptableMainWindows()
|
||||
.flatMap { state in
|
||||
state.tabManager.tabs.flatMap { workspace in
|
||||
workspace.scriptingTerminalPanels().map {
|
||||
ScriptTerminal(workspaceId: workspace.id, terminalId: $0.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc(valueInTerminalsWithUniqueID:)
|
||||
func valueInTerminals(uniqueID: String) -> ScriptTerminal? {
|
||||
guard isAppleScriptEnabled,
|
||||
let terminalId = UUID(uuidString: uniqueID),
|
||||
let appDelegate = AppDelegate.shared else {
|
||||
return nil
|
||||
}
|
||||
|
||||
for state in appDelegate.scriptableMainWindows() {
|
||||
for workspace in state.tabManager.tabs where workspace.terminalPanel(for: terminalId) != nil {
|
||||
return ScriptTerminal(workspaceId: workspace.id, terminalId: terminalId)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc(handlePerformActionScriptCommand:)
|
||||
func handlePerformActionScriptCommand(_ command: NSScriptCommand) -> NSNumber? {
|
||||
guard validateScript(command: command) else { return nil }
|
||||
|
||||
guard let action = command.directParameter as? String else {
|
||||
command.scriptErrorNumber = errAEParamMissed
|
||||
command.scriptErrorString = AppleScriptStrings.missingAction
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let terminal = command.evaluatedArguments?["on"] as? ScriptTerminal else {
|
||||
command.scriptErrorNumber = errAEParamMissed
|
||||
command.scriptErrorString = AppleScriptStrings.missingTerminalTarget
|
||||
return nil
|
||||
}
|
||||
|
||||
return NSNumber(value: terminal.perform(action: action))
|
||||
}
|
||||
|
||||
@objc(handleNewWindowScriptCommand:)
|
||||
func handleNewWindowScriptCommand(_ command: NSScriptCommand) -> ScriptWindow? {
|
||||
guard validateScript(command: command) else { return nil }
|
||||
|
||||
guard let appDelegate = AppDelegate.shared else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.failedToCreateWindow
|
||||
return nil
|
||||
}
|
||||
|
||||
let windowId = appDelegate.createMainWindow()
|
||||
return ScriptWindow(windowId: windowId)
|
||||
}
|
||||
|
||||
@objc(handleNewTabScriptCommand:)
|
||||
func handleNewTabScriptCommand(_ command: NSScriptCommand) -> ScriptTab? {
|
||||
guard validateScript(command: command) else { return nil }
|
||||
|
||||
guard let appDelegate = AppDelegate.shared else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.failedToCreateWorkspace
|
||||
return nil
|
||||
}
|
||||
|
||||
if let targetWindow = command.evaluatedArguments?["window"] as? ScriptWindow {
|
||||
guard let workspaceId = appDelegate.addWorkspace(windowId: targetWindow.windowId, bringToFront: false) else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.failedToCreateWorkspace
|
||||
return nil
|
||||
}
|
||||
return ScriptTab(windowId: targetWindow.windowId, tabId: workspaceId)
|
||||
}
|
||||
|
||||
if let frontWindow = scriptWindows.first,
|
||||
let workspaceId = appDelegate.addWorkspace(windowId: frontWindow.windowId, bringToFront: false) {
|
||||
return ScriptTab(windowId: frontWindow.windowId, tabId: workspaceId)
|
||||
}
|
||||
|
||||
let windowId = appDelegate.createMainWindow()
|
||||
return ScriptWindow(windowId: windowId).selectedTab
|
||||
}
|
||||
|
||||
@objc(handleQuitScriptCommand:)
|
||||
func handleQuitScriptCommand(_ command: NSScriptCommand) {
|
||||
guard validateScript(command: command) else { return }
|
||||
terminate(nil)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@objc(CmuxScriptWindow)
|
||||
final class ScriptWindow: NSObject {
|
||||
let windowId: UUID
|
||||
|
||||
init(windowId: UUID) {
|
||||
self.windowId = windowId
|
||||
}
|
||||
|
||||
private var state: AppDelegate.ScriptableMainWindowState? {
|
||||
AppDelegate.shared?.scriptableMainWindow(windowId: windowId)
|
||||
}
|
||||
|
||||
@objc(id)
|
||||
var idValue: String {
|
||||
guard NSApp.isAppleScriptEnabled else { return "" }
|
||||
return windowId.uuidString
|
||||
}
|
||||
|
||||
@objc(title)
|
||||
var title: String {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let state else {
|
||||
return ""
|
||||
}
|
||||
|
||||
let windowTitle = state.window?.title.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
if !windowTitle.isEmpty {
|
||||
return windowTitle
|
||||
}
|
||||
|
||||
return state.tabManager.selectedWorkspace?.title ?? ""
|
||||
}
|
||||
|
||||
@objc(tabs)
|
||||
var tabs: [ScriptTab] {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let state else {
|
||||
return []
|
||||
}
|
||||
return state.tabManager.tabs.map { ScriptTab(windowId: windowId, tabId: $0.id) }
|
||||
}
|
||||
|
||||
@objc(selectedTab)
|
||||
var selectedTab: ScriptTab? {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let selectedId = state?.tabManager.selectedTabId else {
|
||||
return nil
|
||||
}
|
||||
return ScriptTab(windowId: windowId, tabId: selectedId)
|
||||
}
|
||||
|
||||
@objc(terminals)
|
||||
var terminals: [ScriptTerminal] {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let state else {
|
||||
return []
|
||||
}
|
||||
return state.tabManager.tabs.flatMap { workspace in
|
||||
workspace.scriptingTerminalPanels().map {
|
||||
ScriptTerminal(workspaceId: workspace.id, terminalId: $0.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc(valueInTabsWithUniqueID:)
|
||||
func valueInTabs(uniqueID: String) -> ScriptTab? {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let tabId = UUID(uuidString: uniqueID),
|
||||
let state,
|
||||
state.tabManager.tabs.contains(where: { $0.id == tabId }) else {
|
||||
return nil
|
||||
}
|
||||
return ScriptTab(windowId: windowId, tabId: tabId)
|
||||
}
|
||||
|
||||
@objc(valueInTerminalsWithUniqueID:)
|
||||
func valueInTerminals(uniqueID: String) -> ScriptTerminal? {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let terminalId = UUID(uuidString: uniqueID),
|
||||
let state else {
|
||||
return nil
|
||||
}
|
||||
|
||||
for workspace in state.tabManager.tabs where workspace.terminalPanel(for: terminalId) != nil {
|
||||
return ScriptTerminal(workspaceId: workspace.id, terminalId: terminalId)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc(handleActivateWindowCommand:)
|
||||
func handleActivateWindow(_ command: NSScriptCommand) -> Any? {
|
||||
guard NSApp.validateScript(command: command) else { return nil }
|
||||
|
||||
guard AppDelegate.shared?.focusScriptableMainWindow(windowId: windowId, bringToFront: true) == true else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.windowUnavailable
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc(handleCloseWindowCommand:)
|
||||
func handleCloseWindow(_ command: NSScriptCommand) -> Any? {
|
||||
guard NSApp.validateScript(command: command) else { return nil }
|
||||
|
||||
guard let window = state?.window else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.windowUnavailable
|
||||
return nil
|
||||
}
|
||||
|
||||
window.performClose(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
override var objectSpecifier: NSScriptObjectSpecifier? {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let appClassDescription = NSApplication.shared.classDescription as? NSScriptClassDescription else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NSUniqueIDSpecifier(
|
||||
containerClassDescription: appClassDescription,
|
||||
containerSpecifier: nil,
|
||||
key: "scriptWindows",
|
||||
uniqueID: windowId.uuidString
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@objc(CmuxScriptTab)
|
||||
final class ScriptTab: NSObject {
|
||||
let windowId: UUID
|
||||
let tabId: UUID
|
||||
|
||||
init(windowId: UUID, tabId: UUID) {
|
||||
self.windowId = windowId
|
||||
self.tabId = tabId
|
||||
}
|
||||
|
||||
private var state: AppDelegate.ScriptableMainWindowState? {
|
||||
AppDelegate.shared?.scriptableMainWindow(windowId: windowId)
|
||||
}
|
||||
|
||||
private var workspace: Workspace? {
|
||||
state?.tabManager.tabs.first(where: { $0.id == tabId })
|
||||
}
|
||||
|
||||
private var window: ScriptWindow {
|
||||
ScriptWindow(windowId: windowId)
|
||||
}
|
||||
|
||||
@objc(id)
|
||||
var idValue: String {
|
||||
guard NSApp.isAppleScriptEnabled else { return "" }
|
||||
return tabId.uuidString
|
||||
}
|
||||
|
||||
@objc(title)
|
||||
var title: String {
|
||||
guard NSApp.isAppleScriptEnabled else { return "" }
|
||||
return workspace?.title ?? ""
|
||||
}
|
||||
|
||||
@objc(index)
|
||||
var index: Int {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let state,
|
||||
let idx = state.tabManager.tabs.firstIndex(where: { $0.id == tabId }) else {
|
||||
return 0
|
||||
}
|
||||
return idx + 1
|
||||
}
|
||||
|
||||
@objc(selected)
|
||||
var selected: Bool {
|
||||
guard NSApp.isAppleScriptEnabled else { return false }
|
||||
return state?.tabManager.selectedTabId == tabId
|
||||
}
|
||||
|
||||
@objc(focusedTerminal)
|
||||
var focusedTerminal: ScriptTerminal? {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let terminalId = workspace?.focusedTerminalPanel?.id else {
|
||||
return nil
|
||||
}
|
||||
return ScriptTerminal(workspaceId: tabId, terminalId: terminalId)
|
||||
}
|
||||
|
||||
@objc(terminals)
|
||||
var terminals: [ScriptTerminal] {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let workspace else {
|
||||
return []
|
||||
}
|
||||
return workspace.scriptingTerminalPanels().map {
|
||||
ScriptTerminal(workspaceId: tabId, terminalId: $0.id)
|
||||
}
|
||||
}
|
||||
|
||||
@objc(valueInTerminalsWithUniqueID:)
|
||||
func valueInTerminals(uniqueID: String) -> ScriptTerminal? {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let workspace,
|
||||
let terminalId = UUID(uuidString: uniqueID),
|
||||
workspace.terminalPanel(for: terminalId) != nil else {
|
||||
return nil
|
||||
}
|
||||
return ScriptTerminal(workspaceId: tabId, terminalId: terminalId)
|
||||
}
|
||||
|
||||
@objc(handleSelectTabCommand:)
|
||||
func handleSelectTab(_ command: NSScriptCommand) -> Any? {
|
||||
guard NSApp.validateScript(command: command) else { return nil }
|
||||
|
||||
guard let state,
|
||||
let workspace else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.workspaceUnavailable
|
||||
return nil
|
||||
}
|
||||
|
||||
state.tabManager.selectWorkspace(workspace)
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc(handleCloseTabCommand:)
|
||||
func handleCloseTab(_ command: NSScriptCommand) -> Any? {
|
||||
guard NSApp.validateScript(command: command) else { return nil }
|
||||
|
||||
guard let state,
|
||||
let workspace else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.workspaceUnavailable
|
||||
return nil
|
||||
}
|
||||
|
||||
if state.tabManager.tabs.count > 1 {
|
||||
state.tabManager.closeWorkspace(workspace)
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let window = state.window else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.windowUnavailable
|
||||
return nil
|
||||
}
|
||||
|
||||
window.performClose(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
override var objectSpecifier: NSScriptObjectSpecifier? {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let windowClassDescription = window.classDescription as? NSScriptClassDescription,
|
||||
let windowSpecifier = window.objectSpecifier else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NSUniqueIDSpecifier(
|
||||
containerClassDescription: windowClassDescription,
|
||||
containerSpecifier: windowSpecifier,
|
||||
key: "tabs",
|
||||
uniqueID: tabId.uuidString
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@objc(CmuxScriptTerminal)
|
||||
final class ScriptTerminal: NSObject {
|
||||
let workspaceId: UUID
|
||||
let terminalId: UUID
|
||||
|
||||
init(workspaceId: UUID, terminalId: UUID) {
|
||||
self.workspaceId = workspaceId
|
||||
self.terminalId = terminalId
|
||||
}
|
||||
|
||||
private var state: AppDelegate.ScriptableMainWindowState? {
|
||||
AppDelegate.shared?.scriptableMainWindowForTab(workspaceId)
|
||||
}
|
||||
|
||||
private var workspace: Workspace? {
|
||||
state?.tabManager.tabs.first(where: { $0.id == workspaceId })
|
||||
}
|
||||
|
||||
private var terminal: TerminalPanel? {
|
||||
workspace?.terminalPanel(for: terminalId)
|
||||
}
|
||||
|
||||
@objc(id)
|
||||
var stableID: String {
|
||||
guard NSApp.isAppleScriptEnabled else { return "" }
|
||||
return terminalId.uuidString
|
||||
}
|
||||
|
||||
@objc(title)
|
||||
var title: String {
|
||||
guard NSApp.isAppleScriptEnabled else { return "" }
|
||||
return terminal?.displayTitle ?? ""
|
||||
}
|
||||
|
||||
@objc(workingDirectory)
|
||||
var workingDirectory: String {
|
||||
guard NSApp.isAppleScriptEnabled else { return "" }
|
||||
return terminal?.directory ?? ""
|
||||
}
|
||||
|
||||
func input(text: String) -> Bool {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let terminal else {
|
||||
return false
|
||||
}
|
||||
terminal.sendText(text)
|
||||
return true
|
||||
}
|
||||
|
||||
func perform(action: String) -> Bool {
|
||||
guard NSApp.isAppleScriptEnabled else { return false }
|
||||
return terminal?.performBindingAction(action) ?? false
|
||||
}
|
||||
|
||||
@objc(handleSplitCommand:)
|
||||
func handleSplit(_ command: NSScriptCommand) -> Any? {
|
||||
guard NSApp.validateScript(command: command) else { return nil }
|
||||
|
||||
guard let directionCode = command.evaluatedArguments?["direction"] as? UInt32,
|
||||
let direction = ScriptSplitDirection(code: directionCode)?.splitDirection else {
|
||||
command.scriptErrorNumber = errAEParamMissed
|
||||
command.scriptErrorString = AppleScriptStrings.missingSplitDirection
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let state,
|
||||
let workspace,
|
||||
terminal != nil else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.terminalUnavailable
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let newPanelId = state.tabManager.newSplit(tabId: workspaceId, surfaceId: terminalId, direction: direction),
|
||||
workspace.terminalPanel(for: newPanelId) != nil else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.failedToCreateSplit
|
||||
return nil
|
||||
}
|
||||
|
||||
return ScriptTerminal(workspaceId: workspaceId, terminalId: newPanelId)
|
||||
}
|
||||
|
||||
@objc(handleFocusCommand:)
|
||||
func handleFocus(_ command: NSScriptCommand) -> Any? {
|
||||
guard NSApp.validateScript(command: command) else { return nil }
|
||||
|
||||
guard let state,
|
||||
let workspace,
|
||||
terminal != nil else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.terminalUnavailable
|
||||
return nil
|
||||
}
|
||||
|
||||
if let app = AppDelegate.shared {
|
||||
_ = app.focusScriptableMainWindow(windowId: state.windowId, bringToFront: true)
|
||||
}
|
||||
state.tabManager.selectWorkspace(workspace)
|
||||
workspace.focusPanel(terminalId)
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc(handleCloseCommand:)
|
||||
func handleClose(_ command: NSScriptCommand) -> Any? {
|
||||
guard NSApp.validateScript(command: command) else { return nil }
|
||||
|
||||
guard let state,
|
||||
let workspace,
|
||||
terminal != nil else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.terminalUnavailable
|
||||
return nil
|
||||
}
|
||||
|
||||
if workspace.panels.count == 1 {
|
||||
if state.tabManager.tabs.count > 1 {
|
||||
state.tabManager.closeWorkspace(workspace)
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let window = state.window else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.windowUnavailable
|
||||
return nil
|
||||
}
|
||||
|
||||
window.performClose(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
guard workspace.closePanel(terminalId, force: true) else {
|
||||
command.scriptErrorNumber = errAEEventFailed
|
||||
command.scriptErrorString = AppleScriptStrings.terminalUnavailable
|
||||
return nil
|
||||
}
|
||||
|
||||
AppDelegate.shared?.notificationStore?.clearNotifications(forTabId: workspaceId, surfaceId: terminalId)
|
||||
return nil
|
||||
}
|
||||
|
||||
override var objectSpecifier: NSScriptObjectSpecifier? {
|
||||
guard NSApp.isAppleScriptEnabled,
|
||||
let appClassDescription = NSApplication.shared.classDescription as? NSScriptClassDescription else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NSUniqueIDSpecifier(
|
||||
containerClassDescription: appClassDescription,
|
||||
containerSpecifier: nil,
|
||||
key: "terminals",
|
||||
uniqueID: terminalId.uuidString
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@objc(CmuxScriptInputTextCommand)
|
||||
final class ScriptInputTextCommand: NSScriptCommand {
|
||||
override func performDefaultImplementation() -> Any? {
|
||||
guard NSApp.validateScript(command: self) else { return nil }
|
||||
|
||||
guard let text = directParameter as? String else {
|
||||
scriptErrorNumber = errAEParamMissed
|
||||
scriptErrorString = AppleScriptStrings.missingInputText
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let terminal = evaluatedArguments?["terminal"] as? ScriptTerminal else {
|
||||
scriptErrorNumber = errAEParamMissed
|
||||
scriptErrorString = AppleScriptStrings.missingTerminalTarget
|
||||
return nil
|
||||
}
|
||||
|
||||
guard terminal.input(text: text) else {
|
||||
scriptErrorNumber = errAEEventFailed
|
||||
scriptErrorString = AppleScriptStrings.terminalUnavailable
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private enum ScriptSplitDirection {
|
||||
case right
|
||||
case left
|
||||
case down
|
||||
case up
|
||||
|
||||
init?(code: UInt32) {
|
||||
switch code {
|
||||
case "GSrt".fourCharCode: self = .right
|
||||
case "GSlf".fourCharCode: self = .left
|
||||
case "GSdn".fourCharCode: self = .down
|
||||
case "GSup".fourCharCode: self = .up
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
var splitDirection: SplitDirection {
|
||||
switch self {
|
||||
case .right: return .right
|
||||
case .left: return .left
|
||||
case .down: return .down
|
||||
case .up: return .up
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue