Skip to content

Commit ab17569

Browse files
roomote[bot]Roomrubens
authored
feat: add undo functionality for enhance prompt feature (fixes #5741) (#5742)
Co-authored-by: Roo <[email protected]> Co-authored-by: Matt Rubens <[email protected]>
1 parent 29b7d06 commit ab17569

File tree

3 files changed

+90
-4
lines changed

3 files changed

+90
-4
lines changed

webview-ui/src/components/chat/ChatTextArea.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,25 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
115115
const message = event.data
116116

117117
if (message.type === "enhancedPrompt") {
118-
if (message.text) {
119-
setInputValue(message.text)
118+
if (message.text && textAreaRef.current) {
119+
try {
120+
// Use execCommand to replace text while preserving undo history
121+
if (document.execCommand) {
122+
// Use native browser methods to preserve undo stack
123+
const textarea = textAreaRef.current
124+
125+
// Focus the textarea to ensure it's the active element
126+
textarea.focus()
127+
128+
// Select all text first
129+
textarea.select()
130+
document.execCommand("insertText", false, message.text)
131+
} else {
132+
setInputValue(message.text)
133+
}
134+
} catch {
135+
setInputValue(message.text)
136+
}
120137
}
121138

122139
setIsEnhancingPrompt(false)

webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,27 @@ describe("ChatTextArea", () => {
184184
})
185185

186186
describe("enhanced prompt response", () => {
187-
it("should update input value when receiving enhanced prompt", () => {
187+
it("should update input value using native browser methods when receiving enhanced prompt", () => {
188188
const setInputValue = vi.fn()
189189

190-
render(<ChatTextArea {...defaultProps} setInputValue={setInputValue} />)
190+
// Mock document.execCommand
191+
const mockExecCommand = vi.fn().mockReturnValue(true)
192+
Object.defineProperty(document, "execCommand", {
193+
value: mockExecCommand,
194+
writable: true,
195+
})
196+
197+
const { container } = render(
198+
<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="Original prompt" />,
199+
)
200+
201+
const textarea = container.querySelector("textarea")!
202+
203+
// Mock textarea methods
204+
const mockSelect = vi.fn()
205+
const mockFocus = vi.fn()
206+
textarea.select = mockSelect
207+
textarea.focus = mockFocus
191208

192209
// Simulate receiving enhanced prompt message
193210
window.dispatchEvent(
@@ -199,8 +216,54 @@ describe("ChatTextArea", () => {
199216
}),
200217
)
201218

219+
// Verify native browser methods were used
220+
expect(mockFocus).toHaveBeenCalled()
221+
expect(mockSelect).toHaveBeenCalled()
222+
expect(mockExecCommand).toHaveBeenCalledWith("insertText", false, "Enhanced test prompt")
223+
})
224+
225+
it("should fallback to setInputValue when execCommand is not available", () => {
226+
const setInputValue = vi.fn()
227+
228+
// Mock document.execCommand to be undefined (not available)
229+
Object.defineProperty(document, "execCommand", {
230+
value: undefined,
231+
writable: true,
232+
})
233+
234+
render(<ChatTextArea {...defaultProps} setInputValue={setInputValue} inputValue="Original prompt" />)
235+
236+
// Simulate receiving enhanced prompt message
237+
window.dispatchEvent(
238+
new MessageEvent("message", {
239+
data: {
240+
type: "enhancedPrompt",
241+
text: "Enhanced test prompt",
242+
},
243+
}),
244+
)
245+
246+
// Verify fallback to setInputValue was used
202247
expect(setInputValue).toHaveBeenCalledWith("Enhanced test prompt")
203248
})
249+
250+
it("should not crash when textarea ref is not available", () => {
251+
const setInputValue = vi.fn()
252+
253+
render(<ChatTextArea {...defaultProps} setInputValue={setInputValue} />)
254+
255+
// Simulate receiving enhanced prompt message when textarea ref might not be ready
256+
expect(() => {
257+
window.dispatchEvent(
258+
new MessageEvent("message", {
259+
data: {
260+
type: "enhancedPrompt",
261+
text: "Enhanced test prompt",
262+
},
263+
}),
264+
)
265+
}).not.toThrow()
266+
})
204267
})
205268

206269
describe("multi-file drag and drop", () => {

webview-ui/vitest.setup.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import "@testing-library/jest-dom"
22
import "@testing-library/jest-dom/vitest"
33

4+
// Force React into development mode for tests
5+
// This is needed to enable act(...) function in React Testing Library
6+
globalThis.process = globalThis.process || {}
7+
globalThis.process.env = globalThis.process.env || {}
8+
globalThis.process.env.NODE_ENV = "development"
9+
410
class MockResizeObserver {
511
observe() {}
612
unobserve() {}

0 commit comments

Comments
 (0)