@@ -17,7 +17,7 @@ import { usePromptStash } from "./stash"
1717import { DialogStash } from "../dialog-stash"
1818import { type AutocompleteRef , Autocomplete } from "./autocomplete"
1919import { useCommandDialog } from "../dialog-command"
20- import { useRenderer } from "@opentui/solid"
20+ import { useKeyboard , useRenderer } from "@opentui/solid"
2121import { Editor } from "@tui/util/editor"
2222import { useExit } from "../../context/exit"
2323import { Clipboard } from "../../util/clipboard"
@@ -355,6 +355,20 @@ export function Prompt(props: PromptProps) {
355355 ]
356356 } )
357357
358+ // Windows Terminal 1.25+ with kitty keyboard swallows Ctrl+V press but
359+ // leaks the release (CSI 118;modifier;3u). Detect it and probe clipboard.
360+ if ( process . platform === "win32" ) {
361+ useKeyboard (
362+ ( evt ) => {
363+ if ( ! input . focused ) return
364+ if ( evt . name === "v" && evt . ctrl && evt . eventType === "release" ) {
365+ command . trigger ( "prompt.paste" )
366+ }
367+ } ,
368+ { release : true } ,
369+ )
370+ }
371+
358372 const ref : PromptRef = {
359373 get focused ( ) {
360374 return input . focused
@@ -852,10 +866,8 @@ export function Prompt(props: PromptProps) {
852866 e . preventDefault ( )
853867 return
854868 }
855- // Handle clipboard paste (Ctrl+V) - check for images first on Windows
856- // This is needed because Windows terminal doesn't properly send image data
857- // through bracketed paste, so we need to intercept the keypress and
858- // directly read from clipboard before the terminal handles it
869+ // Check clipboard for images before terminal handles the paste.
870+ // On Windows most terminals consume Ctrl+V so this rarely fires.
859871 if ( keybind . match ( "input_paste" , e ) ) {
860872 const content = await Clipboard . read ( )
861873 if ( content ?. mime . startsWith ( "image/" ) ) {
@@ -938,6 +950,9 @@ export function Prompt(props: PromptProps) {
938950 // Replace CRLF first, then any remaining CR
939951 const normalizedText = event . text . replace ( / \r \n / g, "\n" ) . replace ( / \r / g, "\n" )
940952 const pastedContent = normalizedText . trim ( )
953+
954+ // Empty paste = image-only clipboard. Stable WT sends an empty
955+ // bracketed paste for this; WT 1.25+ with kitty does not.
941956 if ( ! pastedContent ) {
942957 command . trigger ( "prompt.paste" )
943958 return
0 commit comments