Skip to content

Commit 26b8e3a

Browse files
committed
Add toggle for trigger command method, tweak implementation
1 parent 2985b4e commit 26b8e3a

File tree

5 files changed

+75
-36
lines changed

5 files changed

+75
-36
lines changed

Copilot for Xcode/DebugView.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ final class DebugSettings: ObservableObject {
88
@AppStorage(\.preCacheOnFileOpen)
99
var preCacheOnFileOpen: Bool
1010
@AppStorage(\.useCustomScrollViewWorkaround) var useCustomScrollViewWorkaround
11+
@AppStorage(\.triggerActionWithAccessibilityAPI) var triggerActionWithAccessibilityAPI
1112
init() {}
1213
}
1314

@@ -29,6 +30,10 @@ struct DebugSettingsView: View {
2930
Text("Use custom scroll view workaround for smooth scrolling")
3031
}
3132
.toggleStyle(.switch)
33+
Toggle(isOn: $settings.triggerActionWithAccessibilityAPI) {
34+
Text("Trigger action with AccessibilityAPI")
35+
}
36+
.toggleStyle(.switch)
3237
}
3338
}
3439
.buttonStyle(.copilot)

Core/Sources/AXExtension/AXUIElement.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ public extension AXUIElement {
1515
var title: String {
1616
(try? copyValue(key: kAXTitleAttribute)) ?? ""
1717
}
18+
19+
var role: String {
20+
(try? copyValue(key: kAXRoleAttribute)) ?? ""
21+
}
1822

1923
var doubleValue: Double {
2024
(try? copyValue(key: kAXValueAttribute)) ?? 0.0
@@ -130,20 +134,22 @@ public extension AXUIElement {
130134
func child(
131135
identifier: String? = nil,
132136
title: String? = nil,
133-
description: String? = nil
137+
role: String? = nil
134138
) -> AXUIElement? {
135139
for child in children {
136140
let match = {
137141
if let identifier, child.identifier != identifier { return false }
138142
if let title, child.title != title { return false }
139-
if let description, child.description != description { return false }
143+
if let role, child.role != role { return false }
140144
return true
141145
}()
142146
if match { return child }
147+
}
148+
for child in children {
143149
if let target = child.child(
144150
identifier: identifier,
145151
title: title,
146-
description: description
152+
role: role
147153
) { return target }
148154
}
149155
return nil

Core/Sources/Environment/Environment.swift

Lines changed: 55 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import AppKit
33
import AXExtension
44
import CopilotService
55
import Foundation
6+
import Logger
67

78
public struct NoAccessToAccessibilityAPIError: Error, LocalizedError {
89
public var errorDescription: String? {
@@ -120,40 +121,62 @@ public enum Environment {
120121
else { return }
121122
let bundleName = Bundle.main
122123
.object(forInfoDictionaryKey: "EXTENSION_BUNDLE_NAME") as! String
124+
125+
await Task.yield()
126+
127+
if UserDefaults.shared.value(for: \.triggerActionWithAccessibilityAPI) {
128+
if !activeXcode.isActive { activeXcode.activate() }
129+
let app = AXUIElementCreateApplication(activeXcode.processIdentifier)
123130

124-
let app = AXUIElementCreateApplication(activeXcode.processIdentifier)
125-
if let editorMenu = app.menuBar?.child(title: "Editor"),
126-
let commandMenu = editorMenu.child(title: bundleName),
127-
let button = commandMenu.child(title: name, description: "menu bar item")
128-
{
129-
AXUIElementPerformAction(button, "press" as CFString)
130-
} else if let commandMenu = app.menuBar?.child(title: bundleName),
131-
let button = commandMenu.child(title: name, description: "menu bar item")
132-
{
133-
AXUIElementPerformAction(button, "press" as CFString)
131+
if let editorMenu = app.menuBar?.child(title: "Editor"),
132+
let commandMenu = editorMenu.child(title: bundleName)
133+
{
134+
if let button = commandMenu.child(title: name, role: "AXMenuItem") {
135+
let error = AXUIElementPerformAction(button, kAXPressAction as CFString)
136+
if error != AXError.success {
137+
Logger.service
138+
.error("Trigger action \(name) failed: \(error.localizedDescription)")
139+
throw error
140+
}
141+
}
142+
} else if let commandMenu = app.menuBar?.child(title: bundleName),
143+
let button = commandMenu.child(title: name, role: "AXMenuItem")
144+
{
145+
let error = AXUIElementPerformAction(button, kAXPressAction as CFString)
146+
if error != AXError.success {
147+
Logger.service
148+
.error("Trigger action \(name) failed: \(error.localizedDescription)")
149+
throw error
150+
}
151+
}
152+
} else {
153+
/// check if menu is open, if not, click the menu item.
154+
let appleScript = """
155+
tell application "System Events"
156+
set theprocs to every process whose unix id is \(activeXcode.processIdentifier)
157+
repeat with proc in theprocs
158+
set the frontmost of proc to true
159+
tell proc
160+
repeat with theMenu in menus of menu bar 1
161+
set theValue to value of attribute "AXVisibleChildren" of theMenu
162+
if theValue is not {} then
163+
return
164+
end if
165+
end repeat
166+
click menu item "\(name)" of menu 1 of menu item "\(bundleName)" of menu 1 of menu bar item "Editor" of menu bar 1
167+
end tell
168+
end repeat
169+
end tell
170+
"""
171+
172+
do {
173+
try await runAppleScript(appleScript)
174+
} catch {
175+
Logger.service
176+
.error("Trigger action \(name) failed: \(error.localizedDescription)")
177+
throw error
178+
}
134179
}
135-
136-
// /// check if menu is open, if not, click the menu item.
137-
// let appleScript = """
138-
// tell application "System Events"
139-
// set theprocs to every process whose unix id is \(activeXcode.processIdentifier)
140-
// repeat with proc in theprocs
141-
// set the frontmost of proc to true
142-
// tell proc
143-
// repeat with theMenu in menus of menu bar 1
144-
// set theValue to value of attribute "AXVisibleChildren" of theMenu
145-
// if theValue is not {} then
146-
// return
147-
// end if
148-
// end repeat
149-
// click menu item "\(name)" of menu 1 of menu item "\(bundleName)" of menu 1 of
150-
// menu bar item "Editor" of menu bar 1
151-
// end tell
152-
// end repeat
153-
// end tell
154-
// """
155-
//
156-
// try await runAppleScript(appleScript)
157180
}
158181

159182
public static var makeXcodeActive: () async throws -> Void = {

Core/Sources/Preferences/Keys.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,11 @@ public enum FeatureFlags {
281281
public let defaultValue = true
282282
public let key = "FeatureFlag-UseCustomScrollViewWorkaround"
283283
}
284+
285+
public struct TriggerActionWithAccessibilityAPI: UserDefaultPreferenceKey {
286+
public let defaultValue = true
287+
public let key = "FeatureFlag-TriggerActionWithAccessibilityAPI"
288+
}
284289
}
285290

286291
public extension UserDefaultPreferenceKeys {
@@ -289,4 +294,5 @@ public extension UserDefaultPreferenceKeys {
289294
var runNodeWithInteractiveLoggedInShell: FeatureFlags
290295
.RunNodeWithInteractiveLoggedInShell { .init() }
291296
var useCustomScrollViewWorkaround: FeatureFlags.UseCustomScrollViewWorkaround { .init() }
297+
var triggerActionWithAccessibilityAPI: FeatureFlags.TriggerActionWithAccessibilityAPI { .init() }
292298
}

ExtensionService/AppDelegate.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
2828

2929
func applicationDidFinishLaunching(_: Notification) {
3030
if ProcessInfo.processInfo.environment["IS_UNIT_TEST"] == "YES" { return }
31-
Task { try await Environment.triggerAction("") }
3231
_ = GraphicalUserInterfaceController.shared
3332
_ = RealtimeSuggestionController.shared
3433
setupQuitOnUpdate()

0 commit comments

Comments
 (0)