Fix selected text detection and Power Mode handling
This commit is contained in:
parent
05806f4248
commit
d9165e1e4d
@ -19,7 +19,18 @@ class PromptDetectionService {
|
||||
func analyzeText(_ text: String, with enhancementService: AIEnhancementService) -> PromptDetectionResult {
|
||||
let originalEnhancementState = enhancementService.isEnhancementEnabled
|
||||
let originalPromptId = enhancementService.selectedPromptId
|
||||
|
||||
|
||||
if let selectedText = SelectedTextService.fetchSelectedText(), !selectedText.isEmpty {
|
||||
return PromptDetectionResult(
|
||||
shouldEnableAI: true,
|
||||
selectedPromptId: PredefinedPrompts.assistantPromptId,
|
||||
processedText: text, // The user's speech is the prompt for the selected text
|
||||
detectedTriggerWord: nil,
|
||||
originalEnhancementState: originalEnhancementState,
|
||||
originalPromptId: originalPromptId
|
||||
)
|
||||
}
|
||||
|
||||
for prompt in enhancementService.allPrompts {
|
||||
if !prompt.triggerWords.isEmpty {
|
||||
if let (detectedWord, processedText) = findMatchingTriggerWord(from: text, triggerWords: prompt.triggerWords) {
|
||||
@ -34,7 +45,7 @@ class PromptDetectionService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return PromptDetectionResult(
|
||||
shouldEnableAI: false,
|
||||
selectedPromptId: nil,
|
||||
|
||||
@ -3,49 +3,50 @@ import AppKit
|
||||
|
||||
class SelectedTextService {
|
||||
static func fetchSelectedText() -> String? {
|
||||
// Do not check for selected text within VoiceInk itself.
|
||||
guard let frontmostApp = NSWorkspace.shared.frontmostApplication,
|
||||
frontmostApp.bundleIdentifier != "com.prakashjoshipax.VoiceInk" else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// Get the currently focused UI element system-wide.
|
||||
let systemWideElement = AXUIElementCreateSystemWide()
|
||||
var focusedElement: CFTypeRef?
|
||||
|
||||
guard AXUIElementCopyAttributeValue(systemWideElement, kAXFocusedUIElementAttribute as CFString, &focusedElement) == .success,
|
||||
let element = focusedElement else {
|
||||
guard AXUIElementCopyAttributeValue(systemWideElement, kAXFocusedUIElementAttribute as CFString, &focusedElement) == .success, focusedElement != nil else {
|
||||
return nil
|
||||
}
|
||||
let element = focusedElement as! AXUIElement
|
||||
|
||||
// First, try the standard attribute, which is the most reliable method.
|
||||
var selectedTextValue: CFTypeRef?
|
||||
if AXUIElementCopyAttributeValue(element, kAXSelectedTextAttribute as CFString, &selectedTextValue) == .success,
|
||||
let selectedText = selectedTextValue as? String, !selectedText.isEmpty {
|
||||
return selectedText
|
||||
}
|
||||
|
||||
// If the standard attribute fails, fall back to checking the selection range.
|
||||
// This correctly handles apps that don't support the standard attribute.
|
||||
var selectedRangeValue: CFTypeRef?
|
||||
guard AXUIElementCopyAttributeValue(element, kAXSelectedTextRangeAttribute as CFString, &selectedRangeValue) == .success, selectedRangeValue != nil else {
|
||||
return nil
|
||||
}
|
||||
let axRangeValue = selectedRangeValue as! AXValue
|
||||
|
||||
var selectedRange: CFRange = .init()
|
||||
AXValueGetValue(axRangeValue, .cfRange, &selectedRange)
|
||||
|
||||
// An actual selection must have a length greater than zero.
|
||||
guard selectedRange.length > 0 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return findSelectedText(in: element as! AXUIElement)
|
||||
}
|
||||
|
||||
private static func findSelectedText(in element: AXUIElement) -> String? {
|
||||
var selectedTextValue: CFTypeRef?
|
||||
if AXUIElementCopyAttributeValue(element, kAXSelectedTextAttribute as CFString, &selectedTextValue) == .success {
|
||||
if let selectedText = selectedTextValue as? String, !selectedText.isEmpty {
|
||||
return selectedText
|
||||
}
|
||||
var fullTextValue: CFTypeRef?
|
||||
guard AXUIElementCopyAttributeValue(element, kAXValueAttribute as CFString, &fullTextValue) == .success,
|
||||
let fullText = fullTextValue as? String,
|
||||
let subrange = Range(NSRange(location: selectedRange.location, length: selectedRange.length), in: fullText) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fallback for apps that use kAXValueAttribute for selected text (like some Electron apps)
|
||||
var value: CFTypeRef?
|
||||
if AXUIElementCopyAttributeValue(element, kAXValueAttribute as CFString, &value) == .success {
|
||||
if let selectedText = value as? String, !selectedText.isEmpty {
|
||||
return selectedText
|
||||
}
|
||||
}
|
||||
|
||||
var children: CFTypeRef?
|
||||
if AXUIElementCopyAttributeValue(element, kAXChildrenAttribute as CFString, &children) == .success {
|
||||
if let axChildren = children as? [AXUIElement] {
|
||||
for child in axChildren {
|
||||
if let foundText = findSelectedText(in: child) {
|
||||
return foundText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return String(fullText[subrange])
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user