fix: add Ctrl+V support for clipboard image paste in all terminals (#407)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Charles Packer
2025-12-28 20:31:47 -08:00
committed by GitHub
parent aca3d88800
commit 95743ba504

View File

@@ -205,6 +205,33 @@ export function PasteAwareTextInput({
return;
}
// Handle Ctrl+V to check clipboard for images (works in all terminals)
// Native terminals don't send image data via bracketed paste, so we need
// to explicitly check the clipboard when Ctrl+V is pressed.
if (key.ctrl && input === "v") {
const clip = tryImportClipboardImageMac();
if (clip) {
const at = Math.max(
0,
Math.min(caretOffsetRef.current, displayValueRef.current.length),
);
const newDisplay =
displayValueRef.current.slice(0, at) +
clip +
displayValueRef.current.slice(at);
displayValueRef.current = newDisplay;
setDisplayValue(newDisplay);
setActualValue(newDisplay);
onChangeRef.current(newDisplay);
const nextCaret = at + clip.length;
setNudgeCursorOffset(nextCaret);
caretOffsetRef.current = nextCaret;
}
// Don't return - let it fall through to normal paste handling
// in case there's also text in the clipboard
return;
}
// Handle bracketed paste events emitted by vendored Ink
const isPasted = (key as unknown as { isPasted?: boolean })?.isPasted;
if (isPasted) {
@@ -459,6 +486,35 @@ export function PasteAwareTextInput({
return;
}
// Kitty keyboard protocol: Ctrl+V (for clipboard image paste)
// Format: ESC[118;5u (key=118='v', modifier=5=ctrl)
if (sequence === "\x1b[118;5u") {
// Check clipboard for images
const clip = tryImportClipboardImageMac();
if (clip) {
const at = Math.max(
0,
Math.min(caretOffsetRef.current, displayValueRef.current.length),
);
const newDisplay =
displayValueRef.current.slice(0, at) +
clip +
displayValueRef.current.slice(at);
displayValueRef.current = newDisplay;
setDisplayValue(newDisplay);
setActualValue(newDisplay);
onChangeRef.current(newDisplay);
const nextCaret = at + clip.length;
setNudgeCursorOffset(nextCaret);
caretOffsetRef.current = nextCaret;
}
return;
}
// Ignore Ctrl+V key release/repeat events
if (sequence.startsWith("\x1b[118;5:")) {
return;
}
// Kitty keyboard protocol: Arrow keys
// Format: ESC[1;modifier:event_typeX where X is A/B/C/D for up/down/right/left
// Event types: 1=press, 2=repeat, 3=release