fix: prevent enhance prompt from overwriting both edit and new message boxes

- Add context parameter to enhance prompt messages to distinguish between main and edit mode
- Update ChatTextArea to only respond to enhance prompt messages matching its context
- Add comprehensive test coverage for context-specific behavior

Fixes #8553
This commit is contained in:
Roo Code 2025-10-07 18:41:02 +00:00
parent 5a3f911321
commit 994e2a26c5
3 changed files with 127 additions and 23 deletions

View file

@ -1694,7 +1694,12 @@ export const webviewMessageHandler = async (
if (result.success && result.enhancedText) {
// Capture telemetry for prompt enhancement
MessageEnhancer.captureTelemetry(currentCline?.taskId, includeTaskHistoryInEnhance)
await provider.postMessageToWebview({ type: "enhancedPrompt", text: result.enhancedText })
// Include the context from the original request in the response
await provider.postMessageToWebview({
type: "enhancedPrompt",
text: result.enhancedText,
context: message.context,
})
} else {
throw new Error(result.error || "Unknown error")
}
@ -1704,7 +1709,11 @@ export const webviewMessageHandler = async (
)
vscode.window.showErrorMessage(t("common:errors.enhance_prompt"))
await provider.postMessageToWebview({ type: "enhancedPrompt" })
// Include context even in error response
await provider.postMessageToWebview({
type: "enhancedPrompt",
context: message.context,
})
}
}
break

View file

@ -124,28 +124,33 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
const message = event.data
if (message.type === "enhancedPrompt") {
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
// Only handle the enhanced prompt if it's for this specific textarea instance
// Edit mode textareas have context "edit", main textarea has context "main"
const expectedContext = isEditMode ? "edit" : "main"
if (message.context === expectedContext) {
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()
// 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 {
// Select all text first
textarea.select()
document.execCommand("insertText", false, message.text)
} else {
setInputValue(message.text)
}
} catch {
setInputValue(message.text)
}
} catch {
setInputValue(message.text)
}
}
setIsEnhancingPrompt(false)
setIsEnhancingPrompt(false)
}
} else if (message.type === "insertTextIntoTextarea") {
if (message.text && textAreaRef.current) {
// Insert the command text at the current cursor position
@ -196,7 +201,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
window.addEventListener("message", messageHandler)
return () => window.removeEventListener("message", messageHandler)
}, [setInputValue, searchRequestId, inputValue])
}, [setInputValue, searchRequestId, inputValue, isEditMode])
const [isDraggingOver, setIsDraggingOver] = useState(false)
const [textAreaBaseHeight, setTextAreaBaseHeight] = useState<number | undefined>(undefined)
@ -239,11 +244,13 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
if (trimmedInput) {
setIsEnhancingPrompt(true)
vscode.postMessage({ type: "enhancePrompt" as const, text: trimmedInput })
// Include context to identify which textarea is requesting enhancement
const context = isEditMode ? "edit" : "main"
vscode.postMessage({ type: "enhancePrompt" as const, text: trimmedInput, context })
} else {
setInputValue(t("chat:enhancePromptDescription"))
}
}, [inputValue, setInputValue, t])
}, [inputValue, setInputValue, t, isEditMode])
const allModes = useMemo(() => getAllModes(customModes), [customModes])

View file

@ -112,6 +112,7 @@ describe("ChatTextArea", () => {
expect(mockPostMessage).toHaveBeenCalledWith({
type: "enhancePrompt",
text: "Test prompt",
context: "main",
})
})
@ -183,7 +184,7 @@ describe("ChatTextArea", () => {
})
describe("enhanced prompt response", () => {
it("should update input value using native browser methods when receiving enhanced prompt", () => {
it("should update input value using native browser methods when receiving enhanced prompt with matching context", () => {
const setInputValue = vi.fn()
// Mock document.execCommand
@ -205,12 +206,13 @@ describe("ChatTextArea", () => {
textarea.select = mockSelect
textarea.focus = mockFocus
// Simulate receiving enhanced prompt message
// Simulate receiving enhanced prompt message with matching context
window.dispatchEvent(
new MessageEvent("message", {
data: {
type: "enhancedPrompt",
text: "Enhanced test prompt",
context: "main",
},
}),
)
@ -221,6 +223,90 @@ describe("ChatTextArea", () => {
expect(mockExecCommand).toHaveBeenCalledWith("insertText", false, "Enhanced test prompt")
})
it("should ignore enhanced prompt with non-matching context", () => {
const setInputValue = vi.fn()
// 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 with non-matching context (edit instead of main)
window.dispatchEvent(
new MessageEvent("message", {
data: {
type: "enhancedPrompt",
text: "Enhanced test prompt",
context: "edit",
},
}),
)
// Verify native browser methods were NOT called
expect(mockFocus).not.toHaveBeenCalled()
expect(mockSelect).not.toHaveBeenCalled()
expect(mockExecCommand).not.toHaveBeenCalled()
expect(setInputValue).not.toHaveBeenCalled()
})
it("should handle enhanced prompt for edit mode", () => {
const setInputValue = vi.fn()
// 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"
isEditMode={true}
/>,
)
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 with edit context
window.dispatchEvent(
new MessageEvent("message", {
data: {
type: "enhancedPrompt",
text: "Enhanced test prompt",
context: "edit",
},
}),
)
// Verify native browser methods were called for edit mode
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()
@ -238,6 +324,7 @@ describe("ChatTextArea", () => {
data: {
type: "enhancedPrompt",
text: "Enhanced test prompt",
context: "main",
},
}),
)
@ -258,6 +345,7 @@ describe("ChatTextArea", () => {
data: {
type: "enhancedPrompt",
text: "Enhanced test prompt",
context: "main",
},
}),
)