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> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<string>2.06</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>

57
src/InputBoxManager.swift

@ -44,38 +44,8 @@ public final class InputBoxManager {
return 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 /// 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 // 3. Number Formatting
result = applyNumberFormatting(result, format: SonioxSettings.shared.numberFormat) result = applyNumberFormatting(result, format: SonioxSettings.shared.numberFormat)
// 3.5. Sanitize foreign hallucinations & word loops
result = SonioxLiveSession.sanitizeText(result)
// 4. Text Normalization // 4. Text Normalization
if SonioxSettings.shared.normalizeText { if SonioxSettings.shared.normalizeText {
result = normalizeText(result) result = normalizeText(result)
@ -120,6 +123,14 @@ public final class TextProcessor {
private func applyDomainVocabulary(_ text: String) -> String { private func applyDomainVocabulary(_ text: String) -> String {
var t = text var t = text
let terms: [(String, String)] = [ 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"), ("نیو هورایزن", "NewHorizon"),
("نیوهوریزن", "NewHorizon"), ("نیوهوریزن", "NewHorizon"),
@ -247,6 +258,18 @@ public final class TextProcessor {
for p in [".", "،", "؛", "؟", "!"] { for p in [".", "،", "؛", "؟", "!"] {
t = t.replacingOccurrences(of: p, with: p + " ") 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 // Fix spaces inside parentheses / quotes
t = t.replacingOccurrences(of: "( ", with: "(") t = t.replacingOccurrences(of: "( ", with: "(")
t = t.replacingOccurrences(of: "« ", with: "«") t = t.replacingOccurrences(of: "« ", with: "«")

Loading…
Cancel
Save