Skip to content

Commit 2985b4e

Browse files
committed
Support triggering action with accessibility API
1 parent 83fa467 commit 2985b4e

File tree

3 files changed

+60
-25
lines changed

3 files changed

+60
-25
lines changed

Core/Sources/AXExtension/AXUIElement.swift

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ public extension AXUIElement {
1212
(try? copyValue(key: kAXValueAttribute)) ?? ""
1313
}
1414

15+
var title: String {
16+
(try? copyValue(key: kAXTitleAttribute)) ?? ""
17+
}
18+
1519
var doubleValue: Double {
1620
(try? copyValue(key: kAXValueAttribute)) ?? 0.0
1721
}
@@ -115,14 +119,32 @@ public extension AXUIElement {
115119
(try? copyValue(key: kAXChildrenAttribute)) ?? []
116120
}
117121

122+
var menuBar: AXUIElement? {
123+
try? copyValue(key: kAXMenuBarAttribute)
124+
}
125+
118126
var visibleChildren: [AXUIElement] {
119127
(try? copyValue(key: kAXVisibleChildrenAttribute)) ?? []
120128
}
121129

122-
func child(identifier: String) -> AXUIElement? {
130+
func child(
131+
identifier: String? = nil,
132+
title: String? = nil,
133+
description: String? = nil
134+
) -> AXUIElement? {
123135
for child in children {
124-
if child.identifier == identifier { return child }
125-
if let target = child.child(identifier: identifier) { return target }
136+
let match = {
137+
if let identifier, child.identifier != identifier { return false }
138+
if let title, child.title != title { return false }
139+
if let description, child.description != description { return false }
140+
return true
141+
}()
142+
if match { return child }
143+
if let target = child.child(
144+
identifier: identifier,
145+
title: title,
146+
description: description
147+
) { return target }
126148
}
127149
return nil
128150
}

Core/Sources/Environment/Environment.swift

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -121,26 +121,39 @@ public enum Environment {
121121
let bundleName = Bundle.main
122122
.object(forInfoDictionaryKey: "EXTENSION_BUNDLE_NAME") as! String
123123

124-
/// check if menu is open, if not, click the menu item.
125-
let appleScript = """
126-
tell application "System Events"
127-
set theprocs to every process whose unix id is \(activeXcode.processIdentifier)
128-
repeat with proc in theprocs
129-
set the frontmost of proc to true
130-
tell proc
131-
repeat with theMenu in menus of menu bar 1
132-
set theValue to value of attribute "AXVisibleChildren" of theMenu
133-
if theValue is not {} then
134-
return
135-
end if
136-
end repeat
137-
click menu item "\(name)" of menu 1 of menu item "\(bundleName)" of menu 1 of menu bar item "Editor" of menu bar 1
138-
end tell
139-
end repeat
140-
end tell
141-
"""
142-
143-
try await runAppleScript(appleScript)
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)
134+
}
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)
144157
}
145158

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

ExtensionService/AppDelegate.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import ServiceManagement
88
import SwiftUI
99
import UpdateChecker
1010
import UserNotifications
11+
import Environment
1112

1213
let bundleIdentifierBase = Bundle.main
1314
.object(forInfoDictionaryKey: "BUNDLE_IDENTIFIER_BASE") as! String
@@ -27,10 +28,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
2728

2829
func applicationDidFinishLaunching(_: Notification) {
2930
if ProcessInfo.processInfo.environment["IS_UNIT_TEST"] == "YES" { return }
30-
31+
Task { try await Environment.triggerAction("") }
3132
_ = GraphicalUserInterfaceController.shared
3233
_ = RealtimeSuggestionController.shared
33-
UserDefaults.setupDefaultSettings()
3434
setupQuitOnUpdate()
3535
setupQuitOnUserTerminated()
3636
xpcListener = setupXPCListener()

0 commit comments

Comments
 (0)