diff options
| -rw-r--r-- | packages/cli/src/ui/components/InputPrompt.tsx | 5 | ||||
| -rw-r--r-- | packages/cli/src/ui/components/shared/text-buffer.test.ts | 29 | ||||
| -rw-r--r-- | packages/cli/src/ui/components/shared/text-buffer.ts | 25 |
3 files changed, 32 insertions, 27 deletions
diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx index 9f15b56d..8d296dc4 100644 --- a/packages/cli/src/ui/components/InputPrompt.tsx +++ b/packages/cli/src/ui/components/InputPrompt.tsx @@ -164,7 +164,8 @@ export const InputPrompt: React.FC<InputPromptProps> = ({ const handleInput = useCallback( (key: Key) => { - if (!focus) { + /// We want to handle paste even when not focused to support drag and drop. + if (!focus && !key.paste) { return; } @@ -349,7 +350,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({ ], ); - useKeypress(handleInput, { isActive: focus }); + useKeypress(handleInput, { isActive: true }); const linesToRender = buffer.viewportVisualLines; const [cursorVisualRowAbsolute, cursorVisualColAbsolute] = diff --git a/packages/cli/src/ui/components/shared/text-buffer.test.ts b/packages/cli/src/ui/components/shared/text-buffer.test.ts index 89930c18..4db1ce7b 100644 --- a/packages/cli/src/ui/components/shared/text-buffer.test.ts +++ b/packages/cli/src/ui/components/shared/text-buffer.test.ts @@ -407,8 +407,8 @@ describe('useTextBuffer', () => { useTextBuffer({ viewport, isValidPath: () => true }), ); const filePath = '/path/to/a/valid/file.txt'; - act(() => result.current.insert(filePath)); - expect(getBufferState(result).text).toBe(`@${filePath}`); + act(() => result.current.insert(filePath, { paste: true })); + expect(getBufferState(result).text).toBe(`@${filePath} `); }); it('should not prepend @ to an invalid file path on insert', () => { @@ -416,7 +416,7 @@ describe('useTextBuffer', () => { useTextBuffer({ viewport, isValidPath: () => false }), ); const notAPath = 'this is just some long text'; - act(() => result.current.insert(notAPath)); + act(() => result.current.insert(notAPath, { paste: true })); expect(getBufferState(result).text).toBe(notAPath); }); @@ -425,8 +425,8 @@ describe('useTextBuffer', () => { useTextBuffer({ viewport, isValidPath: () => true }), ); const filePath = "'/path/to/a/valid/file.txt'"; - act(() => result.current.insert(filePath)); - expect(getBufferState(result).text).toBe(`@/path/to/a/valid/file.txt`); + act(() => result.current.insert(filePath, { paste: true })); + expect(getBufferState(result).text).toBe(`@/path/to/a/valid/file.txt `); }); it('should not prepend @ to short text that is not a path', () => { @@ -434,7 +434,7 @@ describe('useTextBuffer', () => { useTextBuffer({ viewport, isValidPath: () => true }), ); const shortText = 'ab'; - act(() => result.current.insert(shortText)); + act(() => result.current.insert(shortText, { paste: true })); expect(getBufferState(result).text).toBe(shortText); }); }); @@ -449,7 +449,7 @@ describe('useTextBuffer', () => { }), ); const filePath = '/path/to/a/valid/file.txt'; - act(() => result.current.insert(filePath)); + act(() => result.current.insert(filePath, { paste: true })); expect(getBufferState(result).text).toBe(filePath); // No @ prefix }); @@ -462,7 +462,7 @@ describe('useTextBuffer', () => { }), ); const quotedFilePath = "'/path/to/a/valid/file.txt'"; - act(() => result.current.insert(quotedFilePath)); + act(() => result.current.insert(quotedFilePath, { paste: true })); expect(getBufferState(result).text).toBe(quotedFilePath); // No @ prefix, keeps quotes }); @@ -475,7 +475,7 @@ describe('useTextBuffer', () => { }), ); const notAPath = 'this is just some text'; - act(() => result.current.insert(notAPath)); + act(() => result.current.insert(notAPath, { paste: true })); expect(getBufferState(result).text).toBe(notAPath); }); @@ -488,7 +488,7 @@ describe('useTextBuffer', () => { }), ); const shortText = 'ls'; - act(() => result.current.insert(shortText)); + act(() => result.current.insert(shortText, { paste: true })); expect(getBufferState(result).text).toBe(shortText); // No @ prefix for short text }); }); @@ -849,6 +849,7 @@ describe('useTextBuffer', () => { ctrl: false, meta: false, shift: false, + paste: false, sequence: '\x7f', }); result.current.handleInput({ @@ -856,6 +857,7 @@ describe('useTextBuffer', () => { ctrl: false, meta: false, shift: false, + paste: false, sequence: '\x7f', }); result.current.handleInput({ @@ -863,6 +865,7 @@ describe('useTextBuffer', () => { ctrl: false, meta: false, shift: false, + paste: false, sequence: '\x7f', }); }); @@ -990,9 +993,9 @@ Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots // Simulate pasting the long text multiple times act(() => { - result.current.insert(longText); - result.current.insert(longText); - result.current.insert(longText); + result.current.insert(longText, { paste: true }); + result.current.insert(longText, { paste: true }); + result.current.insert(longText, { paste: true }); }); const state = getBufferState(result); diff --git a/packages/cli/src/ui/components/shared/text-buffer.ts b/packages/cli/src/ui/components/shared/text-buffer.ts index 7eb3088a..31db1f14 100644 --- a/packages/cli/src/ui/components/shared/text-buffer.ts +++ b/packages/cli/src/ui/components/shared/text-buffer.ts @@ -1023,26 +1023,27 @@ export function useTextBuffer({ }, [visualCursor, visualScrollRow, viewport]); const insert = useCallback( - (ch: string): void => { + (ch: string, { paste = false }: { paste?: boolean } = {}): void => { if (/[\n\r]/.test(ch)) { dispatch({ type: 'insert', payload: ch }); return; } const minLengthToInferAsDragDrop = 3; - if (ch.length >= minLengthToInferAsDragDrop && !shellModeActive) { - let potentialPath = ch; - if ( - potentialPath.length > 2 && - potentialPath.startsWith("'") && - potentialPath.endsWith("'") - ) { - potentialPath = ch.slice(1, -1); + if ( + ch.length >= minLengthToInferAsDragDrop && + !shellModeActive && + paste + ) { + let potentialPath = ch.trim(); + const quoteMatch = potentialPath.match(/^'(.*)'$/); + if (quoteMatch) { + potentialPath = quoteMatch[1]; } potentialPath = potentialPath.trim(); if (isValidPath(unescapePath(potentialPath))) { - ch = `@${potentialPath}`; + ch = `@${potentialPath} `; } } @@ -1203,7 +1204,7 @@ export function useTextBuffer({ backspace(); else if (key.name === 'delete' || (key.ctrl && key.name === 'd')) del(); else if (input && !key.ctrl && !key.meta) { - insert(input); + insert(input, { paste: key.paste }); } }, [newline, move, deleteWordLeft, deleteWordRight, backspace, del, insert], @@ -1306,7 +1307,7 @@ export interface TextBuffer { /** * Insert a single character or string without newlines. */ - insert: (ch: string) => void; + insert: (ch: string, opts?: { paste?: boolean }) => void; newline: () => void; backspace: () => void; del: () => void; |
