(input: string, key: Key, event: InputEvent)
| 212 | // the 'readable' listener in App.tsx, causing dropped characters. |
| 213 | |
| 214 | const wrappedOnInput = (input: string, key: Key, event: InputEvent): void => { |
| 215 | // Detect paste from the parsed keypress event. |
| 216 | // The keypress parser sets isPasted=true for content within bracketed paste. |
| 217 | const isFromPaste = event.keypress.isPasted |
| 218 | |
| 219 | // If this is pasted content, set isPasting state for UI feedback |
| 220 | if (isFromPaste) { |
| 221 | setIsPasting(true) |
| 222 | } |
| 223 | |
| 224 | // Handle large pastes (>PASTE_THRESHOLD chars) |
| 225 | // Usually we get one or two input characters at a time. If we |
| 226 | // get more than the threshold, the user has probably pasted. |
| 227 | // Unfortunately node batches long pastes, so it's possible |
| 228 | // that we would see e.g. 1024 characters and then just a few |
| 229 | // more in the next frame that belong with the original paste. |
| 230 | // This batching number is not consistent. |
| 231 | |
| 232 | // Handle potential image filenames (even if they're shorter than paste threshold) |
| 233 | // When dragging multiple images, they may come as newline-separated or |
| 234 | // space-separated paths. Split on spaces preceding absolute paths: |
| 235 | // - Unix: ` /` - Windows: ` C:\` etc. |
| 236 | const hasImageFilePath = input |
| 237 | .split(/ (?=\/|[A-Za-z]:\\)/) |
| 238 | .flatMap(part => part.split('\n')) |
| 239 | .some(line => isImageFilePath(line.trim())) |
| 240 | |
| 241 | // Handle empty paste (clipboard image on macOS) |
| 242 | // When the user pastes an image with Cmd+V, the terminal sends an empty |
| 243 | // bracketed paste sequence. The keypress parser emits this as isPasted=true |
| 244 | // with empty input. |
| 245 | if (isFromPaste && input.length === 0 && isMacOS && onImagePaste) { |
| 246 | checkClipboardForImage() |
| 247 | // Reset isPasting since there's no text content to process |
| 248 | setIsPasting(false) |
| 249 | return |
| 250 | } |
| 251 | |
| 252 | // Check if we should handle as paste (from bracketed paste, large input, or continuation) |
| 253 | const shouldHandleAsPaste = |
| 254 | onPaste && |
| 255 | (input.length > PASTE_THRESHOLD || |
| 256 | pastePendingRef.current || |
| 257 | hasImageFilePath || |
| 258 | isFromPaste) |
| 259 | |
| 260 | if (shouldHandleAsPaste) { |
| 261 | pastePendingRef.current = true |
| 262 | setPasteState(({ chunks, timeoutId }) => { |
| 263 | return { |
| 264 | chunks: [...chunks, input], |
| 265 | timeoutId: resetPasteTimeout(timeoutId), |
| 266 | } |
| 267 | }) |
| 268 | return |
| 269 | } |
| 270 | onInput(input, key) |
| 271 | if (input.length > 10) { |
nothing calls this directly
no test coverage detected