From 1db5b26eec01289acc8072473e722f08ea392f3c Mon Sep 17 00:00:00 2001 From: timl Date: Tue, 29 Jul 2025 16:07:04 -0700 Subject: [PATCH 1/5] Add HighlightedTextManager and pull highlighted text logic into it --- .../AccessibilityNotificationsDelegate.swift | 3 + .../AccessibilityNotificationsManager.swift | 105 ++----------- .../Notifications/WindowChangeDelegate.swift | 5 +- .../PanelStatePinnedManager+Delegates.swift | 7 + .../PanelStateTetheredManager+Delegates.swift | 6 + .../Tethered/PanelStateTetheredManager.swift | 1 + .../HighlightedTextConfig.swift | 12 ++ .../HighlightedTextCoordinator.swift | 0 .../HighlightedTextManager+Delegates.swift | 50 +++++++ .../HighlightedTextManager.swift | 139 ++++++++++++++++++ .../HighlightedTextValidator.swift | 0 .../HighlightedTextWorker.swift | 0 .../Onit/UI/QuickEdit/QuickEditManager.swift | 5 +- 13 files changed, 234 insertions(+), 99 deletions(-) create mode 100644 macos/Onit/StateManagers/HighlightedText/HighlightedTextConfig.swift rename macos/Onit/{Accessibility => StateManagers}/HighlightedText/HighlightedTextCoordinator.swift (100%) create mode 100644 macos/Onit/StateManagers/HighlightedText/HighlightedTextManager+Delegates.swift create mode 100644 macos/Onit/StateManagers/HighlightedText/HighlightedTextManager.swift rename macos/Onit/{Accessibility => StateManagers}/HighlightedText/HighlightedTextValidator.swift (100%) rename macos/Onit/{Accessibility => StateManagers}/HighlightedText/HighlightedTextWorker.swift (100%) diff --git a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsDelegate.swift b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsDelegate.swift index de2cecf84..c1292dab0 100644 --- a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsDelegate.swift +++ b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsDelegate.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Accessibility @MainActor protocol AccessibilityNotificationsDelegate: AnyObject { func accessibilityManager(_ manager: AccessibilityNotificationsManager, didActivateWindow window: TrackedWindow) @@ -18,6 +19,8 @@ import SwiftUI func accessibilityManager(_ manager: AccessibilityNotificationsManager, didResizeWindow window: TrackedWindow) func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeWindowTitle window: TrackedWindow) + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeSelection element: AXUIElement) + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeFocusedUIElement element: AXUIElement) var wantsNotificationsFromIgnoredProcesses: Bool { get } var wantsNotificationsFromOnit: Bool { get } diff --git a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift index 529f1d820..93d8ccb61 100644 --- a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift +++ b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift @@ -28,15 +28,8 @@ class AccessibilityNotificationsManager: ObservableObject { private var currentSource: String? - private var lastHighlightingProcessedAt: Date? - private var lastCaretPositionChangeTimestamp: Date? - private var valueDebounceWorkItem: DispatchWorkItem? - private var selectionDebounceWorkItem: DispatchWorkItem? private var parseDebounceWorkItem: DispatchWorkItem? - - // Published property for selected text that QuickEditManager can observe - @Published var selectedText: String? // MARK: - Private initializer @@ -113,14 +106,9 @@ class AccessibilityNotificationsManager: ObservableObject { } currentSource = nil - lastHighlightingProcessedAt = nil - lastCaretPositionChangeTimestamp = nil valueDebounceWorkItem?.cancel() - selectionDebounceWorkItem?.cancel() parseDebounceWorkItem?.cancel() - selectedText = nil - ContextFetchingService.shared.reset() } @@ -136,10 +124,9 @@ class AccessibilityNotificationsManager: ObservableObject { Task { @MainActor in if let element = unsafeElement { - self.processSelectionChange(for: element) + self.handleSelectionChange(for: element) } else { - // Handle text deselection - clear pendingInput - self.processSelectedText(nil) + // wut } } }) @@ -178,7 +165,7 @@ class AccessibilityNotificationsManager: ObservableObject { self.handleValueChanged(for: element) // self.handleCaretPositionChange(for: element) case kAXFocusedUIElementChangedNotification: - self.handleCaretPositionChange(for: element) + self.handleFocusedUIElementChanged(for: element) case kAXWindowMovedNotification: self.handleWindowMoved(for: element, elementPid: elementPid) case kAXWindowResizedNotification: @@ -276,23 +263,15 @@ class AccessibilityNotificationsManager: ObservableObject { } private func handleSelectionChange(for element: AXUIElement) { - guard HighlightedTextValidator.isValid(element: element) else { return } - - // Fix to work with PDF in Chrome - if let lastHighlightingProcessedAt = lastHighlightingProcessedAt, Date().timeIntervalSince(lastHighlightingProcessedAt) < 0.002 { - return + self.notifyDelegates(for: element) { delegate in + delegate.accessibilityManager(self, didChangeSelection: element) } - - lastHighlightingProcessedAt = Date() - selectionDebounceWorkItem?.cancel() + } - let workItem = DispatchWorkItem { [weak self] in - self?.processSelectionChange(for: element) + private func handleFocusedUIElementChanged(for element: AXUIElement) { + self.notifyDelegates(for: element) { delegate in + delegate.accessibilityManager(self, didChangeFocusedUIElement: element) } - - selectionDebounceWorkItem = workItem - - DispatchQueue.main.asyncAfter(deadline: .now() + Config.textSelectionDebounceInterval, execute: workItem) } // MARK: Value Changed @@ -309,73 +288,7 @@ class AccessibilityNotificationsManager: ObservableObject { showDebug() } - // MARK: Text Selection - - private func processSelectionChange(for element: AXUIElement) { - // Ensure we're on the main thread - dispatchPrecondition(condition: .onQueue(.main)) - let selectedText = element.selectedText() - - if let selectedText = selectedText, HighlightedTextValidator.isValid(text: selectedText) { - Task { - _ = await HighlightedTextBoundsExtractor.shared.getBounds(for: element, selectedText: selectedText) - - processSelectedText(selectedText) - } - } else { - // On every apps, when caret position changed, we receive AXSelectedTextChanged notification with nil value. - // This code is used to hide the QuickEdit hint for a real deselection - let now = Date() - let caretPositionChangeRecently = now.timeIntervalSince(lastCaretPositionChangeTimestamp ?? .distantPast) < 0.5 - let isEditableField = element.role() == kAXTextFieldRole || element.role() == kAXTextAreaRole - - if !caretPositionChangeRecently && !isEditableField { - processSelectedText(nil) - HighlightedTextBoundsExtractor.shared.reset() - QuickEditManager.shared.hideHint() - } else if isEditableField { - handleCaretPositionChange(for: element) - } - } - - showDebug() - } - - private func processSelectedText(_ text: String?) { - guard Defaults[.autoContextFromHighlights], - let selectedText = text, - HighlightedTextValidator.isValid(text: selectedText) else { - - PanelStateCoordinator.shared.state.pendingInput = nil - PanelStateCoordinator.shared.state.trackedPendingInput = nil - self.selectedText = nil - return - } - - // Update the published selectedText property - self.selectedText = selectedText - - let input = Input(selectedText: selectedText, application: currentSource ?? "") - - if Defaults[.autoAddHighlightedTextToContext] { - PanelStateCoordinator.shared.state.pendingInput = input - } else { - PanelStateCoordinator.shared.state.trackedPendingInput = input - } - } - - // MARK: Caret Position Handling - - private func handleCaretPositionChange(for element: AXUIElement) { - guard element.supportsCaretTracking() else { return } - - selectionDebounceWorkItem?.cancel() - selectionDebounceWorkItem = nil - processSelectedText(nil) - lastCaretPositionChangeTimestamp = Date() - caretPositionManager.updateCaretPosition(for: element) - } // MARK: Debug diff --git a/macos/Onit/Accessibility/Notifications/WindowChangeDelegate.swift b/macos/Onit/Accessibility/Notifications/WindowChangeDelegate.swift index 8c8b77934..d650264ff 100644 --- a/macos/Onit/Accessibility/Notifications/WindowChangeDelegate.swift +++ b/macos/Onit/Accessibility/Notifications/WindowChangeDelegate.swift @@ -7,6 +7,7 @@ import Foundation import AppKit +import ApplicationServices // MARK: - Window Change Info @@ -148,7 +149,9 @@ final class WindowChangeDelegate: AccessibilityNotificationsDelegate { func accessibilityManager(_ manager: AccessibilityNotificationsManager, didDeminimizeWindow window: TrackedWindow) {} func accessibilityManager(_ manager: AccessibilityNotificationsManager, didActivateIgnoredWindow window: TrackedWindow?) {} func accessibilityManager(_ manager: AccessibilityNotificationsManager, didDestroyWindow window: TrackedWindow) {} - + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeSelection element: AXUIElement) {} + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeFocusedUIElement element: AXUIElement) {} + var wantsNotificationsFromIgnoredProcesses: Bool { false } var wantsNotificationsFromOnit: Bool { false } } diff --git a/macos/Onit/PanelStateManager/Pinned/PanelStatePinnedManager+Delegates.swift b/macos/Onit/PanelStateManager/Pinned/PanelStatePinnedManager+Delegates.swift index fbf7bca57..01ff6c3cf 100644 --- a/macos/Onit/PanelStateManager/Pinned/PanelStatePinnedManager+Delegates.swift +++ b/macos/Onit/PanelStateManager/Pinned/PanelStatePinnedManager+Delegates.swift @@ -6,6 +6,7 @@ // import AppKit +import ApplicationServices extension PanelStatePinnedManager: AccessibilityNotificationsDelegate { // MARK: - DID ACTIVATE WINDOW @@ -80,6 +81,12 @@ extension PanelStatePinnedManager: AccessibilityNotificationsDelegate { var wantsNotificationsFromIgnoredProcesses: Bool { false } var wantsNotificationsFromOnit: Bool { false } + + // MARK: - Highlighted Text Related Notifications (Not needed for panel state) + + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeSelection element: AXUIElement) { } + + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeFocusedUIElement element: AXUIElement) { } } extension PanelStatePinnedManager: OnitPanelStateDelegate { diff --git a/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager+Delegates.swift b/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager+Delegates.swift index cf726196a..40007d1f5 100644 --- a/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager+Delegates.swift +++ b/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager+Delegates.swift @@ -7,6 +7,9 @@ // MARK: - AccessibilityNotificationsDelegate +import Accessibility +import ApplicationServices + extension PanelStateTetheredManager: AccessibilityNotificationsDelegate { // MARK: - DID ACTIVATE WINDOW @@ -102,6 +105,9 @@ extension PanelStateTetheredManager: AccessibilityNotificationsDelegate { } } + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeSelection element: AXUIElement) { } + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeFocusedUIElement element: AXUIElement) { } + var wantsNotificationsFromIgnoredProcesses: Bool { false } var wantsNotificationsFromOnit: Bool { false } } diff --git a/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager.swift b/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager.swift index 24b064051..d4084cd3f 100644 --- a/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager.swift +++ b/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager.swift @@ -6,6 +6,7 @@ // @preconcurrency import AppKit +import Accessibility import Combine import Defaults import PostHog diff --git a/macos/Onit/StateManagers/HighlightedText/HighlightedTextConfig.swift b/macos/Onit/StateManagers/HighlightedText/HighlightedTextConfig.swift new file mode 100644 index 000000000..030a64022 --- /dev/null +++ b/macos/Onit/StateManagers/HighlightedText/HighlightedTextConfig.swift @@ -0,0 +1,12 @@ +// +// HighlightedTextConfig.swift +// Onit +// +// Created by Kévin Naudin on 28/04/2025. +// + +import Foundation + +struct HighlightedTextConfig { + static let textSelectionDebounceInterval: TimeInterval = 0.15 // 150ms +} \ No newline at end of file diff --git a/macos/Onit/Accessibility/HighlightedText/HighlightedTextCoordinator.swift b/macos/Onit/StateManagers/HighlightedText/HighlightedTextCoordinator.swift similarity index 100% rename from macos/Onit/Accessibility/HighlightedText/HighlightedTextCoordinator.swift rename to macos/Onit/StateManagers/HighlightedText/HighlightedTextCoordinator.swift diff --git a/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager+Delegates.swift b/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager+Delegates.swift new file mode 100644 index 000000000..d59fd5810 --- /dev/null +++ b/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager+Delegates.swift @@ -0,0 +1,50 @@ +// +// HighlightedTextManager.swift +// Onit +// +// Created by TimL on 28/04/2025. +// + +import ApplicationServices + +extension HighlightedTextManager: AccessibilityNotificationsDelegate { + // MARK: - AccessibilityNotificationsDelegate Implementation + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeSelection element: AXUIElement) { + handleSelectionChange(for: element) + } + + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeFocusedUIElement element: AXUIElement) { + handleCaretPositionChange(for: element) + } + + // Unused stubs + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didActivateWindow window: TrackedWindow) {} + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didActivateIgnoredWindow window: TrackedWindow?) {} + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didMinimizeWindow window: TrackedWindow) {} + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didDeminimizeWindow window: TrackedWindow) {} + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didDestroyWindow window: TrackedWindow) {} + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didMoveWindow window: TrackedWindow) {} + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didResizeWindow window: TrackedWindow) {} + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeWindowTitle window: TrackedWindow) {} +} + +extension HighlightedTextManager: AccessibilityObserversDelegate { + + func accessibilityObserversManager(didActivateApplication appName: String?, processID: pid_t) { + setCurrentSource(appName) + } + + func accessibilityObserversManager(didDeactivateApplication appName: String?, processID: pid_t) { + // Reset state when app deactivates + reset() + } + + func accessibilityObserversManager(didActivateIgnoredApplication appName: String?, processID: pid_t) {} + func accessibilityObserversManager(didReceiveNotification notification: String, + element: AXUIElement, + elementPid: pid_t, + info: [String: Any]) {} + func accessibilityObserversManager(didDeactivateIgnoredApplication appName: String?, processID: pid_t) {} + func accessibilityObserversManager(didActivateOnit processID: pid_t) {} + func accessibilityObserversManager(didDeactivateOnit processID: pid_t) {} +} diff --git a/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager.swift b/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager.swift new file mode 100644 index 000000000..c7328965a --- /dev/null +++ b/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager.swift @@ -0,0 +1,139 @@ +// +// HighlightedTextManager.swift +// Onit +// +// Created by TimL on 07/29/2025. +// + +import ApplicationServices +import Defaults +import Foundation +import SwiftUI + +@MainActor +class HighlightedTextManager: ObservableObject { + + // MARK: - Singleton instance + + static let shared = HighlightedTextManager() + + // MARK: - Properties + + private var lastHighlightingProcessedAt: Date? + private var selectionDebounceWorkItem: DispatchWorkItem? + private var currentSource: String? + + // Published property for selected text that QuickEditManager can observe + @Published var selectedText: String? + + // MARK: - Private initializer + + private init() { + AccessibilityNotificationsManager.shared.addDelegate(self) + } + + // MARK: - AccessibilityNotificationsDelegate + + var wantsNotificationsFromIgnoredProcesses: Bool { false } + var wantsNotificationsFromOnit: Bool { false } + + // MARK: - Functions + + func setCurrentSource(_ source: String?) { + currentSource = source + } + + func handleSelectionChange(for element: AXUIElement) { + guard HighlightedTextValidator.isValid(element: element) else { return } + + // Fix to work with PDF in Chrome + if let lastHighlightingProcessedAt = lastHighlightingProcessedAt, Date().timeIntervalSince(lastHighlightingProcessedAt) < 0.002 { + return + } + + lastHighlightingProcessedAt = Date() + selectionDebounceWorkItem?.cancel() + + let workItem = DispatchWorkItem { [weak self] in + self?.processSelectionChange(for: element) + } + + selectionDebounceWorkItem = workItem + + DispatchQueue.main.asyncAfter(deadline: .now() + HighlightedTextConfig.textSelectionDebounceInterval, execute: workItem) + } + + func processSelectionChange(for element: AXUIElement) { + // Ensure we're on the main thread + dispatchPrecondition(condition: .onQueue(.main)) + + let selectedText = element.selectedText() + + if let selectedText = selectedText, HighlightedTextValidator.isValid(text: selectedText) { + Task { + _ = await HighlightedTextBoundsExtractor.shared.getBounds(for: element, selectedText: selectedText) + + processSelectedText(selectedText) + } + } else { + // On every apps, when caret position changed, we receive AXSelectedTextChanged notification with nil value. + // This code is used to hide the QuickEdit hint for a real deselection + let now = Date() + let caretPositionChangeRecently = now.timeIntervalSince(lastCaretPositionChangeTimestamp ?? .distantPast) < 0.5 + let isEditableField = element.role() == kAXTextFieldRole || element.role() == kAXTextAreaRole + + if !caretPositionChangeRecently && !isEditableField { + processSelectedText(nil) + HighlightedTextBoundsExtractor.shared.reset() + QuickEditManager.shared.hideHint() + } else if isEditableField { + handleCaretPositionChange(for: element) + } + } + } + + func processSelectedText(_ text: String?) { + guard Defaults[.autoContextFromHighlights], + let selectedText = text, + HighlightedTextValidator.isValid(text: selectedText) else { + + PanelStateCoordinator.shared.state.pendingInput = nil + PanelStateCoordinator.shared.state.trackedPendingInput = nil + self.selectedText = nil + return + } + + // Update the published selectedText property + self.selectedText = selectedText + + let input = Input(selectedText: selectedText, application: currentSource ?? "") + + if Defaults[.autoAddHighlightedTextToContext] { + PanelStateCoordinator.shared.state.pendingInput = input + } else { + PanelStateCoordinator.shared.state.trackedPendingInput = input + } + } + + func handleCaretPositionChange(for element: AXUIElement) { + guard element.supportsCaretTracking() else { return } + + selectionDebounceWorkItem?.cancel() + selectionDebounceWorkItem = nil + processSelectedText(nil) + lastCaretPositionChangeTimestamp = Date() + CaretPositionManager.shared.updateCaretPosition(for: element) + } + + func reset() { + lastHighlightingProcessedAt = nil + selectionDebounceWorkItem?.cancel() + selectedText = nil + } + + // MARK: - Private Properties + + private var lastCaretPositionChangeTimestamp: Date? + + +} diff --git a/macos/Onit/Accessibility/HighlightedText/HighlightedTextValidator.swift b/macos/Onit/StateManagers/HighlightedText/HighlightedTextValidator.swift similarity index 100% rename from macos/Onit/Accessibility/HighlightedText/HighlightedTextValidator.swift rename to macos/Onit/StateManagers/HighlightedText/HighlightedTextValidator.swift diff --git a/macos/Onit/Accessibility/HighlightedText/HighlightedTextWorker.swift b/macos/Onit/StateManagers/HighlightedText/HighlightedTextWorker.swift similarity index 100% rename from macos/Onit/Accessibility/HighlightedText/HighlightedTextWorker.swift rename to macos/Onit/StateManagers/HighlightedText/HighlightedTextWorker.swift diff --git a/macos/Onit/UI/QuickEdit/QuickEditManager.swift b/macos/Onit/UI/QuickEdit/QuickEditManager.swift index 2d3820841..b023cdef0 100644 --- a/macos/Onit/UI/QuickEdit/QuickEditManager.swift +++ b/macos/Onit/UI/QuickEdit/QuickEditManager.swift @@ -32,6 +32,7 @@ class QuickEditManager: ObservableObject, CaretPositionDelegate { private let hintWindowController = QuickEditHintWindowController() private let caretPositionManager = CaretPositionManager.shared private let accessibilityManager = AccessibilityNotificationsManager.shared + private let highlightedTextManager = HighlightedTextManager.shared private var cancellables = Set() private var currentHintPosition: CGPoint? private var currentHintHeight: CGFloat? @@ -106,8 +107,8 @@ class QuickEditManager: ObservableObject, CaretPositionDelegate { } private func setupTextSelectionMonitoring() { - // Monitor the selectedText property from AccessibilityNotificationsManager - AccessibilityNotificationsManager.shared.$selectedText + // Monitor the selectedText property from HighlightedTextManager + highlightedTextManager.$selectedText .removeDuplicates() .sink { [weak self] selectedText in self?.handleTextSelectionChange(selectedText) From 1959fd7ccc53c7cf868f33997f6f59a5a2884f6b Mon Sep 17 00:00:00 2001 From: timl Date: Tue, 29 Jul 2025 16:07:04 -0700 Subject: [PATCH 2/5] Add HighlightedTextManager and pull highlighted text logic into it --- .../Notifications/AccessibilityNotificationsDelegate.swift | 1 - .../Pinned/PanelStatePinnedManager+Delegates.swift | 7 ++----- .../Tethered/PanelStateTetheredManager+Delegates.swift | 5 ++--- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsDelegate.swift b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsDelegate.swift index c1292dab0..0ca9872c6 100644 --- a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsDelegate.swift +++ b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsDelegate.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Accessibility @MainActor protocol AccessibilityNotificationsDelegate: AnyObject { func accessibilityManager(_ manager: AccessibilityNotificationsManager, didActivateWindow window: TrackedWindow) diff --git a/macos/Onit/PanelStateManager/Pinned/PanelStatePinnedManager+Delegates.swift b/macos/Onit/PanelStateManager/Pinned/PanelStatePinnedManager+Delegates.swift index 01ff6c3cf..fb7bcf2b8 100644 --- a/macos/Onit/PanelStateManager/Pinned/PanelStatePinnedManager+Delegates.swift +++ b/macos/Onit/PanelStateManager/Pinned/PanelStatePinnedManager+Delegates.swift @@ -82,11 +82,8 @@ extension PanelStatePinnedManager: AccessibilityNotificationsDelegate { var wantsNotificationsFromIgnoredProcesses: Bool { false } var wantsNotificationsFromOnit: Bool { false } - // MARK: - Highlighted Text Related Notifications (Not needed for panel state) - - func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeSelection element: AXUIElement) { } - - func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeFocusedUIElement element: AXUIElement) { } + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeSelection element: AXUIElement) {} + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeFocusedUIElement element: AXUIElement) {} } extension PanelStatePinnedManager: OnitPanelStateDelegate { diff --git a/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager+Delegates.swift b/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager+Delegates.swift index 40007d1f5..388eb9d89 100644 --- a/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager+Delegates.swift +++ b/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager+Delegates.swift @@ -7,7 +7,6 @@ // MARK: - AccessibilityNotificationsDelegate -import Accessibility import ApplicationServices extension PanelStateTetheredManager: AccessibilityNotificationsDelegate { @@ -105,8 +104,8 @@ extension PanelStateTetheredManager: AccessibilityNotificationsDelegate { } } - func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeSelection element: AXUIElement) { } - func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeFocusedUIElement element: AXUIElement) { } + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeSelection element: AXUIElement) {} + func accessibilityManager(_ manager: AccessibilityNotificationsManager, didChangeFocusedUIElement element: AXUIElement) {} var wantsNotificationsFromIgnoredProcesses: Bool { false } var wantsNotificationsFromOnit: Bool { false } From 610edd2482ccdfdb25284ee34251d0377d8698d3 Mon Sep 17 00:00:00 2001 From: Tim L Date: Tue, 29 Jul 2025 16:41:34 -0700 Subject: [PATCH 3/5] Apply suggestion from @greptile-apps[bot] Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- .../HighlightedText/HighlightedTextManager+Delegates.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager+Delegates.swift b/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager+Delegates.swift index d59fd5810..925736a32 100644 --- a/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager+Delegates.swift +++ b/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager+Delegates.swift @@ -1,5 +1,5 @@ // -// HighlightedTextManager.swift +// HighlightedTextManager+Delegates.swift // Onit // // Created by TimL on 28/04/2025. From e85110aa8aaf3fad1c6819e617b95ae4f8496a56 Mon Sep 17 00:00:00 2001 From: Niduank Date: Thu, 31 Jul 2025 14:20:15 +0200 Subject: [PATCH 4/5] Review fixes --- .../Notifications/AccessibilityNotificationsManager.swift | 2 +- .../PanelStateManager/Tethered/PanelStateTetheredManager.swift | 1 - macos/Onit/UI/QuickEdit/QuickEditManager.swift | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift index 93d8ccb61..f2be22bf8 100644 --- a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift +++ b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift @@ -126,7 +126,7 @@ class AccessibilityNotificationsManager: ObservableObject { if let element = unsafeElement { self.handleSelectionChange(for: element) } else { - // wut + HighlightedTextManager.shared.processSelectedText(nil) } } }) diff --git a/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager.swift b/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager.swift index d4084cd3f..24b064051 100644 --- a/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager.swift +++ b/macos/Onit/PanelStateManager/Tethered/PanelStateTetheredManager.swift @@ -6,7 +6,6 @@ // @preconcurrency import AppKit -import Accessibility import Combine import Defaults import PostHog diff --git a/macos/Onit/UI/QuickEdit/QuickEditManager.swift b/macos/Onit/UI/QuickEdit/QuickEditManager.swift index b023cdef0..8aa1da888 100644 --- a/macos/Onit/UI/QuickEdit/QuickEditManager.swift +++ b/macos/Onit/UI/QuickEdit/QuickEditManager.swift @@ -31,7 +31,6 @@ class QuickEditManager: ObservableObject, CaretPositionDelegate { private let windowController = QuickEditWindowController() private let hintWindowController = QuickEditHintWindowController() private let caretPositionManager = CaretPositionManager.shared - private let accessibilityManager = AccessibilityNotificationsManager.shared private let highlightedTextManager = HighlightedTextManager.shared private var cancellables = Set() private var currentHintPosition: CGPoint? From 05026157340265146d39b67cf42f354fed7b9186 Mon Sep 17 00:00:00 2001 From: timl Date: Tue, 5 Aug 2025 13:42:38 -0700 Subject: [PATCH 5/5] address nits --- .../AccessibilityNotificationsManager+Config.swift | 2 +- .../Notifications/AccessibilityNotificationsManager.swift | 1 - .../HighlightedText/HighlightedTextManager.swift | 7 +------ 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager+Config.swift b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager+Config.swift index 845b432fc..8a296506f 100644 --- a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager+Config.swift +++ b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager+Config.swift @@ -12,6 +12,6 @@ extension AccessibilityNotificationsManager { struct Config { static let debounceInterval: TimeInterval = 0.3 // 300ms - static let textSelectionDebounceInterval: TimeInterval = 0.15 // 150ms + } } diff --git a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift index f2be22bf8..9f79516ed 100644 --- a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift +++ b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift @@ -163,7 +163,6 @@ class AccessibilityNotificationsManager: ObservableObject { self.handleSelectionChange(for: element) case kAXValueChangedNotification: self.handleValueChanged(for: element) - // self.handleCaretPositionChange(for: element) case kAXFocusedUIElementChangedNotification: self.handleFocusedUIElementChanged(for: element) case kAXWindowMovedNotification: diff --git a/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager.swift b/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager.swift index c7328965a..21edb3a0e 100644 --- a/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager.swift +++ b/macos/Onit/StateManagers/HighlightedText/HighlightedTextManager.swift @@ -22,6 +22,7 @@ class HighlightedTextManager: ObservableObject { private var lastHighlightingProcessedAt: Date? private var selectionDebounceWorkItem: DispatchWorkItem? private var currentSource: String? + private var lastCaretPositionChangeTimestamp: Date? // Published property for selected text that QuickEditManager can observe @Published var selectedText: String? @@ -130,10 +131,4 @@ class HighlightedTextManager: ObservableObject { selectionDebounceWorkItem?.cancel() selectedText = nil } - - // MARK: - Private Properties - - private var lastCaretPositionChangeTimestamp: Date? - - }