forked from intitni/CopilotForXcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAppActivator.swift
More file actions
123 lines (104 loc) · 4.08 KB
/
AppActivator.swift
File metadata and controls
123 lines (104 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import AppKit
import Dependencies
import XcodeInspector
public extension NSWorkspace {
static func activateThisApp(delay: TimeInterval = 0.10) {
Task { @MainActor in
try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
// NSApp.activate may fail. And since macOS 14, it looks like the app needs other
// apps to call `yieldActivationToApplication` to activate itself?
let activated = NSRunningApplication.current
.activate(options: [.activateIgnoringOtherApps])
if activated { return }
// Fallback solution
let axApplication = AXUIElementCreateApplication(
ProcessInfo.processInfo.processIdentifier
)
activateAppElement(axApplication)
//
// let appleScript = """
// tell application "System Events"
// set frontmost of the first process whose unix id is \
// \(ProcessInfo.processInfo.processIdentifier) to true
// end tell
// """
// try await runAppleScript(appleScript)
}
}
static func activatePreviousActiveApp(delay: TimeInterval = 0.2) {
Task { @MainActor in
guard let app = XcodeInspector.shared.previousActiveApplication
else { return }
try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
activateApp(app)
}
}
static func activatePreviousActiveXcode(delay: TimeInterval = 0.2) {
Task { @MainActor in
guard let app = XcodeInspector.shared.latestActiveXcode else { return }
try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
activateApp(app)
}
}
static func activateApp(_ app: AppInstanceInspector) {
// we prefer `.activate()` because it only brings the active window to the front
if !app.activate() {
activateAppElement(app.appElement)
}
}
static func activateAppElement(_ appElement: AXUIElement) {
appElement.isFrontmost = true
}
}
struct ActivateThisAppDependencyKey: DependencyKey {
static var liveValue: () -> Void = { NSWorkspace.activateThisApp() }
}
struct ActivatePreviousActiveAppDependencyKey: DependencyKey {
static var liveValue: () -> Void = { NSWorkspace.activatePreviousActiveApp() }
}
struct ActivatePreviousActiveXcodeDependencyKey: DependencyKey {
static var liveValue: () -> Void = { NSWorkspace.activatePreviousActiveXcode() }
}
public extension DependencyValues {
var activateThisApp: () -> Void {
get { self[ActivateThisAppDependencyKey.self] }
set { self[ActivateThisAppDependencyKey.self] = newValue }
}
var activatePreviousActiveApp: () -> Void {
get { self[ActivatePreviousActiveAppDependencyKey.self] }
set { self[ActivatePreviousActiveAppDependencyKey.self] = newValue }
}
var activatePreviousActiveXcode: () -> Void {
get { self[ActivatePreviousActiveXcodeDependencyKey.self] }
set { self[ActivatePreviousActiveXcodeDependencyKey.self] = newValue }
}
}
@discardableResult
func runAppleScript(_ appleScript: String) async throws -> String {
let task = Process()
task.launchPath = "/usr/bin/osascript"
task.arguments = ["-e", appleScript]
let outpipe = Pipe()
task.standardOutput = outpipe
task.standardError = Pipe()
return try await withUnsafeThrowingContinuation { continuation in
do {
task.terminationHandler = { _ in
do {
if let data = try outpipe.fileHandleForReading.readToEnd(),
let content = String(data: data, encoding: .utf8)
{
continuation.resume(returning: content)
return
}
continuation.resume(returning: "")
} catch {
continuation.resume(throwing: error)
}
}
try task.run()
} catch {
continuation.resume(throwing: error)
}
}
}