diff --git a/package-lock.json b/package-lock.json index 6cf5d3f2..4cc3325f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8162,6 +8162,7 @@ "read-package-up": "^11.0.0", "shell-quote": "^1.8.2", "string-width": "^7.1.0", + "strip-ansi": "^7.1.0", "yargs": "^17.7.2" }, "bin": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 727f16e3..433b9a58 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -43,8 +43,9 @@ "react": "^18.3.1", "read-package-up": "^11.0.0", "shell-quote": "^1.8.2", - "yargs": "^17.7.2", - "string-width": "^7.1.0" + "string-width": "^7.1.0", + "strip-ansi": "^7.1.0", + "yargs": "^17.7.2" }, "devDependencies": { "@testing-library/react": "^14.0.0", 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 8e35e3e9..acaa4179 100644 --- a/packages/cli/src/ui/components/shared/text-buffer.test.ts +++ b/packages/cli/src/ui/components/shared/text-buffer.test.ts @@ -495,6 +495,14 @@ describe('useTextBuffer', () => { act(() => result.current.handleInput(undefined, { rightArrow: true })); // cursor [0,2] expect(getBufferState(result).cursor).toEqual([0, 2]); }); + + it('should strip ANSI escape codes when pasting text', () => { + const { result } = renderHook(() => useTextBuffer({ viewport })); + const textWithAnsi = '\x1B[31mHello\x1B[0m \x1B[32mWorld\x1B[0m'; + // Simulate pasting by calling handleInput with a string longer than 1 char + act(() => result.current.handleInput(textWithAnsi, {})); + expect(getBufferState(result).text).toBe('Hello World'); + }); }); // More tests would be needed for: diff --git a/packages/cli/src/ui/components/shared/text-buffer.ts b/packages/cli/src/ui/components/shared/text-buffer.ts index 5f25f9e0..b13a57c6 100644 --- a/packages/cli/src/ui/components/shared/text-buffer.ts +++ b/packages/cli/src/ui/components/shared/text-buffer.ts @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import stripAnsi from 'strip-ansi'; import { spawnSync } from 'child_process'; import fs from 'fs'; import os from 'os'; @@ -1127,7 +1128,12 @@ export function useTextBuffer({ ) backspace(); else if (key['delete']) del(); - else if (input && !key['ctrl'] && !key['meta']) insert(input); + else if (input && !key['ctrl'] && !key['meta']) { + // Heuristic for paste: if input is longer than 1 char (potential paste) + // strip ANSI escape codes. + const cleanedInput = input.length > 1 ? stripAnsi(input) : input; + insert(cleanedInput); + } const textChanged = text !== beforeText; // After operations, visualCursor might not be immediately updated if the change