diff --git a/extensions/cli/src/ui/TextBuffer.test.ts b/extensions/cli/src/ui/TextBuffer.test.ts index a9d539ed327..c6cc18cda53 100644 --- a/extensions/cli/src/ui/TextBuffer.test.ts +++ b/extensions/cli/src/ui/TextBuffer.test.ts @@ -526,12 +526,31 @@ describe("TextBuffer", () => { buffer.handleInput(largeChunk, { ctrl: false, meta: false } as any); // Small typed input should not be mixed into paste + const smallChunk = "abc"; + buffer.handleInput(smallChunk, { ctrl: false, meta: false } as any); + expect(buffer.text).toBe(""); // Both chunks buffered + + buffer.flushPendingInput(); + expect(buffer.text).toBe(largeChunk + smallChunk); + }); + + it("should allow typing after paste accumulation delay", () => { + vi.useFakeTimers(); + + const largeChunk = "x".repeat(100); + buffer.handleInput(largeChunk, { ctrl: false, meta: false } as any); + + // Advance time to simulate user typing after a delay + vi.advanceTimersByTime(60); // so that it is treated as typing + const typedChar = "a"; buffer.handleInput(typedChar, { ctrl: false, meta: false } as any); - expect(buffer.text).toBe("a"); + expect(buffer.text).toBe("a"); // Typed char inserted immediately - buffer.flushPendingInput(); + vi.advanceTimersByTime(250); // Finalize paste expect(buffer.text).toBe("a" + largeChunk); + + vi.useRealTimers(); }); it("should preserve all chunks when expanding (content loss fix)", () => { diff --git a/extensions/cli/src/ui/TextBuffer.ts b/extensions/cli/src/ui/TextBuffer.ts index 65784e852ef..768ab0b4b60 100644 --- a/extensions/cli/src/ui/TextBuffer.ts +++ b/extensions/cli/src/ui/TextBuffer.ts @@ -337,26 +337,24 @@ export class TextBuffer { const timeSinceLastInput = now - this._lastInputTime; // If we're already in rapid input mode and this comes quickly, add to buffer - // But only if it's a reasonably large chunk - small inputs are likely typing, not paste - // This prevents typed characters from being mixed into paste content during accumulation - if ( - this._rapidInputBuffer.length > 0 && - timeSinceLastInput < 200 && - input.length >= 50 - ) { - this._rapidInputBuffer += input; - this._lastInputTime = now; + if (this._rapidInputBuffer.length > 0 && timeSinceLastInput < 200) { + const isLikelyTyping = input.length < 50 && timeSinceLastInput >= 50; - if (this._rapidInputTimer) { - clearTimeout(this._rapidInputTimer); - } + if (!isLikelyTyping) { + this._rapidInputBuffer += input; + this._lastInputTime = now; - // Reset timer: 200ms pause indicates end of paste - this._rapidInputTimer = setTimeout(() => { - this.finalizeRapidInput(); - }, 200); + if (this._rapidInputTimer) { + clearTimeout(this._rapidInputTimer); + } - return true; + // Reset timer: 200ms pause indicates end of paste + this._rapidInputTimer = setTimeout(() => { + this.finalizeRapidInput(); + }, 200); + + return true; + } } // Fallback paste detection: some terminals send large pastes as rapid chunks