Skip to content

Commit f4258b1

Browse files
committed
fix(tui): support macos image paste shortcut
1 parent f874251 commit f4258b1

3 files changed

Lines changed: 41 additions & 7 deletions

File tree

.changeset/macos-image-paste.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@moonshot-ai/kimi-code": patch
3+
---
4+
5+
Allow Cmd+V to paste clipboard images on macOS.

apps/kimi-code/src/tui/components/editor/custom-editor.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ function getNewlineInput(data: string): string | undefined {
111111
return undefined;
112112
}
113113

114+
function isPasteImageShortcut(data: string): boolean {
115+
if (process.platform === 'win32') return matchesKey(data, Key.alt('v'));
116+
if (matchesKey(data, Key.ctrl('v'))) return true;
117+
return process.platform === 'darwin' && matchesKey(data, Key.super('v'));
118+
}
119+
114120
export class CustomEditor extends Editor {
115121
public onEscape?: () => void;
116122
public onCtrlD?: () => void;
@@ -132,8 +138,8 @@ export class CustomEditor extends Editor {
132138
public connectedAbove = false;
133139
public borderHighlighted = false;
134140
/**
135-
* Called when the user triggers "paste image" (Ctrl-V on Unix,
136-
* Alt-V on Windows — Ctrl-V is terminal-reserved there). Return
141+
* Called when the user triggers "paste image" (Ctrl-V on non-Windows,
142+
* plus Cmd-V on macOS and Alt-V on Windows). Return
137143
* `true` to consume the key (image was read and handled); return
138144
* `false` to let the key fall through to the normal paste path.
139145
* The callback may be async; pi-tui awaits it before dispatching
@@ -273,11 +279,11 @@ export class CustomEditor extends Editor {
273279
// Paste image binding — platform-aware:
274280
// Windows terminals reserve Ctrl-V for their own paste handling
275281
// (e.g. Windows Terminal's Ctrl+V shortcut), so we listen for
276-
// Alt-V there. Everywhere else Ctrl-V pastes. When the host
277-
// reports no image available, we fall through to pi-tui's
278-
// normal paste path so text from the clipboard still works.
279-
const pasteKey = process.platform === 'win32' ? 'alt+v' : Key.ctrl('v');
280-
if (matchesKey(normalized, pasteKey)) {
282+
// Alt-V there. Non-Windows keeps Ctrl-V, and macOS also accepts
283+
// Cmd-V (Kitty "super").
284+
// When the host reports no image available, we fall through to
285+
// pi-tui's normal paste path so text from the clipboard still works.
286+
if (isPasteImageShortcut(normalized)) {
281287
if (this.expandPasteMarkerAtCursor()) {
282288
return;
283289
}

apps/kimi-code/test/tui/components/editor/custom-editor.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,29 @@ describe('CustomEditor paste marker expansion', () => {
255255
});
256256
});
257257

258+
describe('CustomEditor image paste shortcuts', () => {
259+
it('handles macOS Cmd+V as an image paste shortcut', async () => {
260+
const originalPlatform = process.platform;
261+
Object.defineProperty(process, 'platform', { value: 'darwin', configurable: true });
262+
try {
263+
const editor = makeEditor();
264+
const onPasteImage = vi.fn(async () => true);
265+
const onTextPaste = vi.fn();
266+
editor.onPasteImage = onPasteImage;
267+
editor.onTextPaste = onTextPaste;
268+
269+
editor.handleInput('\u001B[118;9u');
270+
await Promise.resolve();
271+
272+
expect(onPasteImage).toHaveBeenCalledOnce();
273+
expect(onTextPaste).not.toHaveBeenCalled();
274+
expect(editor.getText()).toBe('');
275+
} finally {
276+
Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
277+
}
278+
});
279+
});
280+
258281
describe('CustomEditor shortcut telemetry hooks', () => {
259282
it('reports newline shortcuts, including Ctrl-J, before delegating to the base editor', () => {
260283
const editor = makeEditor();

0 commit comments

Comments
 (0)