@@ -24,6 +24,7 @@ final class InlineSuggestionManager {
2424 private var currentTask : Task < Void , Never > ?
2525 private let _keyEventMonitor = OSAllocatedUnfairLock < Any ? > ( initialState: nil )
2626 private let _scrollObserver = OSAllocatedUnfairLock < Any ? > ( initialState: nil )
27+ private( set) var isEditorFocused = false
2728
2829 deinit {
2930 if let monitor = _keyEventMonitor. withLock ( { $0 } ) { NSEvent . removeMonitor ( monitor) }
@@ -57,25 +58,35 @@ final class InlineSuggestionManager {
5758 func install( controller: TextViewController , schemaProvider: SQLSchemaProvider ? ) {
5859 self . controller = controller
5960 self . schemaProvider = schemaProvider
60- installKeyEventMonitor ( )
6161 installScrollObserver ( )
6262 }
6363
64+ func editorDidFocus( ) {
65+ guard !isEditorFocused else { return }
66+ isEditorFocused = true
67+ installKeyEventMonitor ( )
68+ }
69+
70+ func editorDidBlur( ) {
71+ guard isEditorFocused else { return }
72+ isEditorFocused = false
73+ dismissSuggestion ( )
74+ removeKeyEventMonitor ( )
75+ }
76+
6477 /// Remove all observers and layers
6578 func uninstall( ) {
6679 guard !isUninstalled else { return }
6780 isUninstalled = true
81+ isEditorFocused = false
6882
6983 debounceTimer? . invalidate ( )
7084 debounceTimer = nil
7185 currentTask? . cancel ( )
7286 currentTask = nil
7387 removeGhostLayer ( )
7488
75- if let monitor = _keyEventMonitor. withLock ( { $0 } ) {
76- NSEvent . removeMonitor ( monitor)
77- _keyEventMonitor. withLock { $0 = nil }
78- }
89+ removeKeyEventMonitor ( )
7990
8091 if let observer = _scrollObserver. withLock ( { $0 } ) {
8192 NotificationCenter . default. removeObserver ( observer)
@@ -362,13 +373,14 @@ final class InlineSuggestionManager {
362373 // MARK: - Key Event Monitor
363374
364375 private func installKeyEventMonitor( ) {
376+ removeKeyEventMonitor ( )
365377 _keyEventMonitor. withLock { $0 = NSEvent . addLocalMonitorForEvents ( matching: . keyDown) { [ weak self] event in
366- guard let self else { return event }
378+ guard let self, self . isEditorFocused else { return event }
379+
380+ guard AppSettingsManager . shared. ai. inlineSuggestEnabled else { return event }
367381
368- // Only intercept when a suggestion is active
369382 guard self . currentSuggestion != nil else { return event }
370383
371- // Only intercept when our text view is the first responder
372384 guard let textView = self . controller? . textView,
373385 event. window === textView. window,
374386 textView. window? . firstResponder === textView else { return event }
@@ -378,25 +390,30 @@ final class InlineSuggestionManager {
378390 Task { @MainActor [ weak self] in
379391 self ? . acceptSuggestion ( )
380392 }
381- return nil // Consume the event
393+ return nil
382394
383395 case 53 : // Escape — dismiss suggestion
384396 Task { @MainActor [ weak self] in
385397 self ? . dismissSuggestion ( )
386398 }
387- return nil // Consume the event
399+ return nil
388400
389401 default :
390- // Any other key — dismiss and pass through
391- // The text change handler will schedule a new suggestion
392402 Task { @MainActor [ weak self] in
393403 self ? . dismissSuggestion ( )
394404 }
395- return event // Pass through
405+ return event
396406 }
397407 } }
398408 }
399409
410+ private func removeKeyEventMonitor( ) {
411+ _keyEventMonitor. withLock {
412+ if let monitor = $0 { NSEvent . removeMonitor ( monitor) }
413+ $0 = nil
414+ }
415+ }
416+
400417 // MARK: - Scroll Observer
401418
402419 private func installScrollObserver( ) {
0 commit comments