From ecbeb60ca5327186301900ccef208143ff46ef22 Mon Sep 17 00:00:00 2001 From: Samuel Huang Date: Fri, 14 Aug 2026 11:29:38 -0400 Subject: [PATCH] macos: send all insertText commits as key events Previously, insertText commits without marked text were delivered via sendText, which applies paste semantics and wraps the text in bracketed paste when the program enables it. macOS dictation and other input methods often commit without marked text, so programs treated dictated text as a paste and applied paste-specific handling. insertText is only invoked by input methods (IME, dictation, emoji picker, character viewer); real paste operations use a separate path. Send every non-empty commit through the key event path so programs interpret input method text as typed input. Typing is unaffected (the accumulator path returns earlier) and Cmd+V pastes are unaffected. The helper is renamed from committedPreeditTextAction to committedTextAction since it no longer only handles preedit commits. --- .../Surface View/SurfaceView_AppKit.swift | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift index 35b99e436..0581f7aeb 100644 --- a/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift @@ -1212,7 +1212,7 @@ extension Ghostty { continue } - _ = committedPreeditTextAction(action, text: text) + _ = committedTextAction(action, text: text) } if shouldReplayCommittedPreeditKey(translationEvent) { @@ -1512,7 +1512,7 @@ extension Ghostty { } } - private func committedPreeditTextAction( + private func committedTextAction( _ action: ghostty_input_action_e, text: String ) -> Bool { @@ -2063,7 +2063,6 @@ extension Ghostty.SurfaceView: NSTextInputClient { func insertText(_ string: Any, replacementRange: NSRange) { // We must have an associated event guard NSApp.currentEvent != nil else { return } - guard let surfaceModel else { return } // We want the string view of the any value var chars = "" @@ -2089,8 +2088,6 @@ extension Ghostty.SurfaceView: NSTextInputClient { return } - let hadMarkedText = hasMarkedText() - // If insertText is called, our preedit must be over. unmarkText() @@ -2102,14 +2099,11 @@ extension Ghostty.SurfaceView: NSTextInputClient { return } - if hadMarkedText, !chars.isEmpty { - // Send preedit commits as key events instead of raw text for - // keybind interpretation by programs. - _ = committedPreeditTextAction(GHOSTTY_ACTION_PRESS, text: chars) - return + // All committed text (IME, dictation, etc.) must be sent as key + // events so programs treat it as typed input, never as a paste. + if !chars.isEmpty { + _ = committedTextAction(GHOSTTY_ACTION_PRESS, text: chars) } - - surfaceModel.sendText(chars) } /// This function needs to exist for two reasons: