Skip to content

Commit 5d16a49

Browse files
committed
Add target AXExtension
1 parent a20c54c commit 5d16a49

File tree

2 files changed

+127
-1
lines changed

2 files changed

+127
-1
lines changed

Core/Package.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ let package = Package(
6868
"AXNotificationStream",
6969
"Environment",
7070
"SuggestionWidget",
71+
"AXExtension",
7172
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
7273
]
7374
),
@@ -93,7 +94,7 @@ let package = Package(
9394
.target(name: "AXNotificationStream"),
9495
.target(
9596
name: "Environment",
96-
dependencies: ["ActiveApplicationMonitor", "CopilotService"]
97+
dependencies: ["ActiveApplicationMonitor", "CopilotService", "AXExtension"]
9798
),
9899
.target(
99100
name: "SuggestionWidget",
@@ -104,5 +105,6 @@ let package = Package(
104105
]
105106
),
106107
.target(name: "UpdateChecker"),
108+
.target(name: "AXExtension"),
107109
]
108110
)
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import AppKit
2+
import Foundation
3+
4+
public extension AXUIElement {
5+
var identifier: String {
6+
(try? copyValue(key: kAXIdentifierAttribute)) ?? ""
7+
}
8+
9+
var value: String {
10+
(try? copyValue(key: kAXValueAttribute)) ?? ""
11+
}
12+
13+
var document: String? {
14+
try? copyValue(key: kAXDocumentAttribute)
15+
}
16+
17+
var focusedElement: AXUIElement? {
18+
try? copyValue(key: kAXFocusedUIElementAttribute)
19+
}
20+
21+
var description: String {
22+
(try? copyValue(key: kAXDescriptionAttribute)) ?? ""
23+
}
24+
25+
var selectedTextRange: Range<Int>? {
26+
guard let value: AXValue = try? copyValue(key: kAXSelectedTextRangeAttribute)
27+
else { return nil }
28+
var range: CFRange = .init(location: 0, length: 0)
29+
if AXValueGetValue(value, .cfRange, &range) {
30+
return Range(.init(location: range.location, length: range.length))
31+
}
32+
return nil
33+
}
34+
35+
var sharedFocusElements: [AXUIElement] {
36+
(try? copyValue(key: kAXChildrenAttribute)) ?? []
37+
}
38+
39+
var window: AXUIElement? {
40+
try? copyValue(key: kAXWindowAttribute)
41+
}
42+
43+
var windows: [AXUIElement] {
44+
(try? copyValue(key: kAXWindowsAttribute)) ?? []
45+
}
46+
47+
var focusedWindow: AXUIElement? {
48+
try? copyValue(key: kAXFocusedWindowAttribute)
49+
}
50+
51+
var topLevelElement: AXUIElement? {
52+
try? copyValue(key: kAXTopLevelUIElementAttribute)
53+
}
54+
55+
var rows: [AXUIElement] {
56+
(try? copyValue(key: kAXRowsAttribute)) ?? []
57+
}
58+
59+
var parent: AXUIElement? {
60+
try? copyValue(key: kAXParentAttribute)
61+
}
62+
63+
var children: [AXUIElement] {
64+
(try? copyValue(key: kAXChildrenAttribute)) ?? []
65+
}
66+
67+
var visibleChildren: [AXUIElement] {
68+
(try? copyValue(key: kAXVisibleChildrenAttribute)) ?? []
69+
}
70+
71+
var isFocused: Bool {
72+
(try? copyValue(key: kAXFocusedAttribute)) ?? false
73+
}
74+
75+
var isEnabled: Bool {
76+
(try? copyValue(key: kAXEnabledAttribute)) ?? false
77+
}
78+
79+
func child(identifier: String) -> AXUIElement? {
80+
for child in children {
81+
if child.identifier == identifier { return child }
82+
if let target = child.child(identifier: identifier) { return target }
83+
}
84+
return nil
85+
}
86+
87+
func visibleChild(identifier: String) -> AXUIElement? {
88+
for child in visibleChildren {
89+
if child.identifier == identifier { return child }
90+
if let target = child.visibleChild(identifier: identifier) { return target }
91+
}
92+
return nil
93+
}
94+
95+
func copyValue<T>(key: String, ofType _: T.Type = T.self) throws -> T {
96+
var value: AnyObject?
97+
let error = AXUIElementCopyAttributeValue(self, key as CFString, &value)
98+
if error == .success, let value = value as? T {
99+
return value
100+
}
101+
throw error
102+
}
103+
104+
func copyParameterizedValue<T>(
105+
key: String,
106+
parameters: AnyObject,
107+
ofType _: T.Type = T.self
108+
) throws -> T {
109+
var value: AnyObject?
110+
let error = AXUIElementCopyParameterizedAttributeValue(
111+
self,
112+
key as CFString,
113+
parameters as CFTypeRef,
114+
&value
115+
)
116+
if error == .success, let value = value as? T {
117+
return value
118+
}
119+
throw error
120+
}
121+
122+
}
123+
124+
extension AXError: Error {}

0 commit comments

Comments
 (0)