Soniox Mobile to Mac - Real-time Voice Dictation & Remote Input Control
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

107 lines
3.8 KiB

import Cocoa
import ApplicationServices
// Comprehensive recursive search for any element with AXFocused == true
func findActiveFocusedElement(_ elem: AXUIElement) -> AXUIElement? {
var isFocusedObj: CFTypeRef?
if AXUIElementCopyAttributeValue(elem, kAXFocusedAttribute as CFString, &isFocusedObj) == .success,
let isFocused = isFocusedObj as? Bool, isFocused {
var roleObj: CFTypeRef?
AXUIElementCopyAttributeValue(elem, kAXRoleAttribute as CFString, &roleObj)
let role = (roleObj as? String) ?? ""
if role != "AXWindow" && role != "AXApplication" && role != "AXGroup" && role != "AXSplitGroup" && role != "AXScrollArea" {
return elem
}
}
var childrenObj: CFTypeRef?
if AXUIElementCopyAttributeValue(elem, kAXChildrenAttribute as CFString, &childrenObj) == .success,
let children = childrenObj as? [AXUIElement] {
for child in children {
if let found = findActiveFocusedElement(child) {
return found
}
}
}
return nil
}
func getFocusedTextInfo() -> (String, String, Int, Int)? {
guard let frontApp = NSWorkspace.shared.frontmostApplication else { return nil }
let appName = frontApp.localizedName ?? "App"
let appElem = AXUIElementCreateApplication(frontApp.processIdentifier)
// 1. Try systemWide
let sysWide = AXUIElementCreateSystemWide()
var focusedElemObj: CFTypeRef?
var targetElem: AXUIElement?
if AXUIElementCopyAttributeValue(sysWide, kAXFocusedUIElementAttribute as CFString, &focusedElemObj) == .success,
let obj = focusedElemObj {
targetElem = (obj as! AXUIElement)
}
// 2. Try App focused element
if targetElem == nil {
if AXUIElementCopyAttributeValue(appElem, kAXFocusedUIElementAttribute as CFString, &focusedElemObj) == .success,
let obj = focusedElemObj {
targetElem = (obj as! AXUIElement)
}
}
// 3. Try Recursive search in app tree
if targetElem == nil {
targetElem = findActiveFocusedElement(appElem)
}
guard let elem = targetElem else { return nil }
// Extract text
var text = ""
var valObj: CFTypeRef?
if AXUIElementCopyAttributeValue(elem, kAXValueAttribute as CFString, &valObj) == .success,
let val = valObj {
if let str = val as? String {
text = str
} else if let attrStr = val as? NSAttributedString {
text = attrStr.string
}
}
// Try parameterized string for range
if text.isEmpty {
var countObj: CFTypeRef?
if AXUIElementCopyAttributeValue(elem, kAXNumberOfCharactersAttribute as CFString, &countObj) == .success,
let count = countObj as? Int, count > 0 {
var range = CFRange(location: 0, length: count)
if let axRange = AXValueCreate(.cfRange, &range) {
var strObj: CFTypeRef?
if AXUIElementCopyParameterizedAttributeValue(elem, kAXStringForRangeParameterizedAttribute as CFString, axRange, &strObj) == .success,
let str = strObj as? String {
text = str
}
}
}
}
// Cursor & Selection
var cursor = text.count
var selLen = 0
var rangeObj: CFTypeRef?
if AXUIElementCopyAttributeValue(elem, kAXSelectedTextRangeAttribute as CFString, &rangeObj) == .success,
let axRange = rangeObj {
var range = CFRange()
if AXValueGetValue(axRange as! AXValue, .cfRange, &range) {
cursor = range.location
selLen = range.length
}
}
return (appName, text, cursor, selLen)
}
if let (app, text, cursor, selLen) = getFocusedTextInfo() {
print("Found! App: \(app), Text: '\(text)', Cursor: \(cursor), SelLen: \(selLen)")
} else {
print("No focused text element found")
}