@@ -26,6 +26,7 @@ final class SQLEditorCoordinator: TextViewCoordinator {
2626 @ObservationIgnored private var contextMenu : AIEditorContextMenu ?
2727 @ObservationIgnored private var inlineSuggestionManager : InlineSuggestionManager ?
2828 @ObservationIgnored private var editorSettingsObserver : NSObjectProtocol ?
29+ @ObservationIgnored private var windowKeyObserver : NSObjectProtocol ?
2930 /// Debounce work item for frame-change notification to avoid
3031 /// triggering syntax highlight viewport recalculation on every keystroke.
3132 @ObservationIgnored private var frameChangeWorkItem : DispatchWorkItem ?
@@ -61,6 +62,9 @@ final class SQLEditorCoordinator: TextViewCoordinator {
6162 if let observer = editorSettingsObserver {
6263 NotificationCenter . default. removeObserver ( observer)
6364 }
65+ if let observer = windowKeyObserver {
66+ NotificationCenter . default. removeObserver ( observer)
67+ }
6468 frameChangeWorkItem? . cancel ( )
6569 }
6670
@@ -69,6 +73,10 @@ final class SQLEditorCoordinator: TextViewCoordinator {
6973 NotificationCenter . default. removeObserver ( observer)
7074 editorSettingsObserver = nil
7175 }
76+ if let observer = windowKeyObserver {
77+ NotificationCenter . default. removeObserver ( observer)
78+ windowKeyObserver = nil
79+ }
7280 frameChangeWorkItem? . cancel ( )
7381 frameChangeWorkItem = nil
7482 }
@@ -89,6 +97,22 @@ final class SQLEditorCoordinator: TextViewCoordinator {
8997 self . installVimModeIfEnabled ( controller: controller)
9098 if let textView = controller. textView {
9199 EditorEventRouter . shared. register ( self , textView: textView)
100+
101+ // Auto-focus: make the editor first responder, then ensure a
102+ // cursor exists. Order matters — setCursorPositions calls
103+ // updateSelectionViews which guards on isFirstResponder.
104+ if let window = textView. window {
105+ window. makeFirstResponder ( textView)
106+ }
107+ if controller. cursorPositions. isEmpty {
108+ controller. setCursorPositions ( [ CursorPosition ( range: NSRange ( location: 0 , length: 0 ) ) ] )
109+ }
110+
111+ // Recreate cursor views when the window regains key status.
112+ // resignKeyWindow() on the text view calls removeCursors() which
113+ // destroys cursor subviews, but becomeKeyWindow() only resets the
114+ // blink timer without recreating them.
115+ self . installWindowKeyObserver ( for: textView. window)
92116 }
93117 }
94118 }
@@ -280,6 +304,24 @@ final class SQLEditorCoordinator: TextViewCoordinator {
280304 }
281305 }
282306
307+ // MARK: - Window Key Observer
308+
309+ /// Observe when the editor's window regains key status (e.g. tab switch) and
310+ /// recreate cursor views that were destroyed by resignKeyWindow → removeCursors.
311+ private func installWindowKeyObserver( for window: NSWindow ? ) {
312+ guard let window else { return }
313+ windowKeyObserver = NotificationCenter . default. addObserver (
314+ forName: NSWindow . didBecomeKeyNotification,
315+ object: window,
316+ queue: . main
317+ ) { [ weak self, weak controller] _ in
318+ guard let self, let controller, !controller. cursorPositions. isEmpty else { return }
319+ // At this point becomeKeyWindow → becomeFirstResponder has already run,
320+ // so isFirstResponder is true and setCursorPositions will create cursor views.
321+ controller. setCursorPositions ( controller. cursorPositions)
322+ }
323+ }
324+
283325 // MARK: - Horizontal Scrolling Fix
284326
285327 /// Enable horizontal scrolling when word wrap is off.
0 commit comments