diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index a38b4538d05..7b404ddccb6 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -115,8 +115,25 @@ const ChatTextArea = forwardRef( const message = event.data if (message.type === "enhancedPrompt") { - if (message.text) { - setInputValue(message.text) + if (message.text && textAreaRef.current) { + try { + // Use execCommand to replace text while preserving undo history + if (document.execCommand) { + // Use native browser methods to preserve undo stack + const textarea = textAreaRef.current + + // Focus the textarea to ensure it's the active element + textarea.focus() + + // Select all text first + textarea.select() + document.execCommand("insertText", false, message.text) + } else { + setInputValue(message.text) + } + } catch { + setInputValue(message.text) + } } setIsEnhancingPrompt(false) diff --git a/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx b/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx index 75324c97f4b..973420207c7 100644 --- a/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx +++ b/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx @@ -184,10 +184,27 @@ describe("ChatTextArea", () => { }) describe("enhanced prompt response", () => { - it("should update input value when receiving enhanced prompt", () => { + it("should update input value using native browser methods when receiving enhanced prompt", () => { const setInputValue = vi.fn() - render() + // Mock document.execCommand + const mockExecCommand = vi.fn().mockReturnValue(true) + Object.defineProperty(document, "execCommand", { + value: mockExecCommand, + writable: true, + }) + + const { container } = render( + , + ) + + const textarea = container.querySelector("textarea")! + + // Mock textarea methods + const mockSelect = vi.fn() + const mockFocus = vi.fn() + textarea.select = mockSelect + textarea.focus = mockFocus // Simulate receiving enhanced prompt message window.dispatchEvent( @@ -199,8 +216,54 @@ describe("ChatTextArea", () => { }), ) + // Verify native browser methods were used + expect(mockFocus).toHaveBeenCalled() + expect(mockSelect).toHaveBeenCalled() + expect(mockExecCommand).toHaveBeenCalledWith("insertText", false, "Enhanced test prompt") + }) + + it("should fallback to setInputValue when execCommand is not available", () => { + const setInputValue = vi.fn() + + // Mock document.execCommand to be undefined (not available) + Object.defineProperty(document, "execCommand", { + value: undefined, + writable: true, + }) + + render() + + // Simulate receiving enhanced prompt message + window.dispatchEvent( + new MessageEvent("message", { + data: { + type: "enhancedPrompt", + text: "Enhanced test prompt", + }, + }), + ) + + // Verify fallback to setInputValue was used expect(setInputValue).toHaveBeenCalledWith("Enhanced test prompt") }) + + it("should not crash when textarea ref is not available", () => { + const setInputValue = vi.fn() + + render() + + // Simulate receiving enhanced prompt message when textarea ref might not be ready + expect(() => { + window.dispatchEvent( + new MessageEvent("message", { + data: { + type: "enhancedPrompt", + text: "Enhanced test prompt", + }, + }), + ) + }).not.toThrow() + }) }) describe("multi-file drag and drop", () => { diff --git a/webview-ui/vitest.setup.ts b/webview-ui/vitest.setup.ts index afa37bd96d4..12210f0ec20 100644 --- a/webview-ui/vitest.setup.ts +++ b/webview-ui/vitest.setup.ts @@ -1,6 +1,12 @@ import "@testing-library/jest-dom" import "@testing-library/jest-dom/vitest" +// Force React into development mode for tests +// This is needed to enable act(...) function in React Testing Library +globalThis.process = globalThis.process || {} +globalThis.process.env = globalThis.process.env || {} +globalThis.process.env.NODE_ENV = "development" + class MockResizeObserver { observe() {} unobserve() {}