Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions webview-ui/src/components/chat/ChatTextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,25 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
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)
Expand Down
67 changes: 65 additions & 2 deletions webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(<ChatTextArea {...defaultProps} setInputValue={setInputValue} />)
// Mock document.execCommand
const mockExecCommand = vi.fn().mockReturnValue(true)
Object.defineProperty(document, "execCommand", {
value: mockExecCommand,
writable: true,
})

const { container } = render(
<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="Original prompt" />,
)

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(
Expand All @@ -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(<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="Original prompt" />)

// 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(<ChatTextArea {...defaultProps} setInputValue={setInputValue} />)

// 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", () => {
Expand Down
6 changes: 6 additions & 0 deletions webview-ui/vitest.setup.ts
Original file line number Diff line number Diff line change
@@ -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() {}
Expand Down