Skip to content

Commit ef1f42a

Browse files
committed
Workaround cooperative app activation
1 parent 1ca4993 commit ef1f42a

File tree

3 files changed

+53
-21
lines changed

3 files changed

+53
-21
lines changed

Core/Sources/Service/GUI/GraphicalUserInterfaceController.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,7 @@ struct GUI: ReducerProtocol {
141141
)
142142
await send(.suggestionWidget(.updateKeyWindow(.chatPanel)))
143143

144-
if await !NSApplication.shared.isActive {
145-
activateThisApp()
146-
}
144+
activateThisApp()
147145
}
148146

149147
case .createChatGPTChatTabIfNeeded:

Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -197,17 +197,6 @@ public struct WidgetFeature: ReducerProtocol {
197197
PanelFeature()
198198
}
199199

200-
Reduce { _, action in
201-
switch action {
202-
case .panel(.sharedPanel(.promptToCodeGroup(.activateOrCreatePromptToCode))):
203-
return .run { send in
204-
await send(.updateKeyWindow(.sharedPanel))
205-
activateThisApp()
206-
}
207-
default: return .none
208-
}
209-
}
210-
211200
Scope(state: \.chatPanelState, action: /Action.chatPanel) {
212201
ChatPanelFeature()
213202
}

Tool/Sources/AppActivator/AppActivator.swift

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,27 @@ import Dependencies
33
import XcodeInspector
44

55
public extension NSWorkspace {
6-
static func activateThisApp(delay: TimeInterval = 0.5) {
6+
static func activateThisApp(delay: TimeInterval = 0.3) {
77
Task { @MainActor in
88
try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
9-
// NSApp.activate may fail.
10-
NSRunningApplication(
11-
processIdentifier: ProcessInfo.processInfo.processIdentifier
12-
)?.activate()
9+
10+
// NSApp.activate may fail. And since macOS 14, it looks like the app needs other
11+
// apps to call `yieldActivationToApplication` to activate itself?
12+
13+
let activated = NSRunningApplication.current
14+
.activate(options: [.activateIgnoringOtherApps])
15+
16+
if activated { return }
17+
18+
// Fallback solution
19+
20+
let appleScript = """
21+
tell application "System Events"
22+
set frontmost of the first process whose unix id is \
23+
\(ProcessInfo.processInfo.processIdentifier) to true
24+
end tell
25+
"""
26+
try await runAppleScript(appleScript)
1327
}
1428
}
1529

@@ -20,7 +34,7 @@ public extension NSWorkspace {
2034
app.runningApplication.activate()
2135
}
2236
}
23-
37+
2438
static func activatePreviousActiveXcode(delay: TimeInterval = 0.2) {
2539
Task { @MainActor in
2640
guard let app = XcodeInspector.shared.latestActiveXcode else { return }
@@ -52,10 +66,41 @@ public extension DependencyValues {
5266
get { self[ActivatePreviousActiveAppDependencyKey.self] }
5367
set { self[ActivatePreviousActiveAppDependencyKey.self] = newValue }
5468
}
55-
69+
5670
var activatePreviousActiveXcode: () -> Void {
5771
get { self[ActivatePreviousActiveXcodeDependencyKey.self] }
5872
set { self[ActivatePreviousActiveXcodeDependencyKey.self] = newValue }
5973
}
6074
}
6175

76+
@discardableResult
77+
func runAppleScript(_ appleScript: String) async throws -> String {
78+
let task = Process()
79+
task.launchPath = "/usr/bin/osascript"
80+
task.arguments = ["-e", appleScript]
81+
let outpipe = Pipe()
82+
task.standardOutput = outpipe
83+
task.standardError = Pipe()
84+
85+
return try await withUnsafeThrowingContinuation { continuation in
86+
do {
87+
task.terminationHandler = { _ in
88+
do {
89+
if let data = try outpipe.fileHandleForReading.readToEnd(),
90+
let content = String(data: data, encoding: .utf8)
91+
{
92+
continuation.resume(returning: content)
93+
return
94+
}
95+
continuation.resume(returning: "")
96+
} catch {
97+
continuation.resume(throwing: error)
98+
}
99+
}
100+
try task.run()
101+
} catch {
102+
continuation.resume(throwing: error)
103+
}
104+
}
105+
}
106+

0 commit comments

Comments
 (0)