Browse Source

fix(input-box): eliminate focus stealing to browser address bar on hotkey trigger (v2.06)

main
Ali Alavi 1 week ago
parent
commit
c169c62838
  1. 2
      Info.plist
  2. 57
      src/InputBoxManager.swift
  3. 23
      src/TextProcessor.swift

2
Info.plist

@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<string>2.06</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>

57
src/InputBoxManager.swift

@ -44,38 +44,8 @@ public final class InputBoxManager {
return
}
guard let frontApp = NSWorkspace.shared.frontmostApplication else { return }
if let bid = frontApp.bundleIdentifier?.lowercased(),
bid.contains("siri") || bid.contains("shortcuts") {
print("InputBoxManager: Frontmost app is Siri/Shortcuts (\(bid)), preserving user app focus.")
return
}
let appElem = AXUIElementCreateApplication(frontApp.processIdentifier)
// 1. Check if an element is already focused in the frontmost application
var focusedObj: CFTypeRef?
if AXUIElementCopyAttributeValue(appElem, kAXFocusedUIElementAttribute as CFString, &focusedObj) == .success,
focusedObj != nil {
return
}
// 2. Check systemWide focus
let sysWide = AXUIElementCreateSystemWide()
if AXUIElementCopyAttributeValue(sysWide, kAXFocusedUIElementAttribute as CFString, &focusedObj) == .success,
focusedObj != nil {
return
}
// 3. Only if NOTHING is focused, search active window and gently set focus
var windowObj: CFTypeRef?
if AXUIElementCopyAttributeValue(appElem, kAXFocusedWindowAttribute as CFString, &windowObj) == .success,
let win = windowObj {
let winElem = (win as! AXUIElement)
if let bestInput = findBestInputElement(in: winElem) {
print("InputBoxManager: No active focus detected. Gently focusing target input field in \(frontApp.localizedName ?? "").")
AXUIElementSetAttributeValue(bestInput, kAXFocusedAttribute as CFString, kCFBooleanTrue)
}
}
// Focus is 100% user-directed. We preserve whatever the user focused (web text input,
// native text box, code editor, or address bar). Never forcibly steal focus to any toolbar or omnibox.
}
/// Streams word-by-word delta text directly into the focused input box in real time
@ -408,26 +378,5 @@ public final class InputBoxManager {
}
}
private func findBestInputElement(in parent: AXUIElement, maxDepth: Int = 8, depth: Int = 0) -> AXUIElement? {
if depth > maxDepth { return nil }
var roleObj: CFTypeRef?
AXUIElementCopyAttributeValue(parent, kAXRoleAttribute as CFString, &roleObj)
let role = (roleObj as? String) ?? ""
if role == "AXTextField" || role == "AXTextArea" || role == "AXComboBox" {
return parent
}
var childrenObj: CFTypeRef?
if AXUIElementCopyAttributeValue(parent, kAXChildrenAttribute as CFString, &childrenObj) == .success,
let children = childrenObj as? [AXUIElement] {
for child in children {
if let found = findBestInputElement(in: child, maxDepth: maxDepth, depth: depth + 1) {
return found
}
}
}
return nil
}
}

23
src/TextProcessor.swift

@ -49,6 +49,9 @@ public final class TextProcessor {
// 3. Number Formatting
result = applyNumberFormatting(result, format: SonioxSettings.shared.numberFormat)
// 3.5. Sanitize foreign hallucinations & word loops
result = SonioxLiveSession.sanitizeText(result)
// 4. Text Normalization
if SonioxSettings.shared.normalizeText {
result = normalizeText(result)
@ -120,6 +123,14 @@ public final class TextProcessor {
private func applyDomainVocabulary(_ text: String) -> String {
var t = text
let terms: [(String, String)] = [
("این اپ پرچس", "In-App Purchase"),
("این اپ پرشرز", "In-App Purchase"),
("اپ پرشرز", "In-App Purchases"),
("اپل کانکت", "App Store Connect"),
("اپ استور کانکت", "App Store Connect"),
("حجزی مرچنت", "Hajzee Merchant"),
("حجزی یوزر", "Hajzee User"),
("حجزی", "Hajzee"),
("نیوهورایزن", "NewHorizon"),
("نیو هورایزن", "NewHorizon"),
("نیوهوریزن", "NewHorizon"),
@ -247,6 +258,18 @@ public final class TextProcessor {
for p in [".", "،", "؛", "؟", "!"] {
t = t.replacingOccurrences(of: p, with: p + " ")
}
// Common ASR stutter & merged word fixes
let speechFixes: [(String, String)] = [
(" ماا ", " ما "),
(" اینپیگیری ", " این پیگیری "),
(" می‌کنکنیم ", " می‌کنیم "),
(" سر سمانش ", " سر و سامانش "),
(" دست و پا گیرلی ", " دست و پا گیر ولی ")
]
for (bad, good) in speechFixes {
t = t.replacingOccurrences(of: bad, with: good)
}
// Fix spaces inside parentheses / quotes
t = t.replacingOccurrences(of: "( ", with: "(")
t = t.replacingOccurrences(of: "« ", with: "«")

Loading…
Cancel
Save