diff --git a/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx b/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx
index f53bab76a4..a361643b3e 100644
--- a/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx
+++ b/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx
@@ -602,6 +602,70 @@ describe("ChatTextArea", () => {
expect(setInputValue).toHaveBeenCalledWith("New input")
})
+ it("should allow navigating back to original input after typing in history mode", () => {
+ const setInputValue = vi.fn()
+ const { container } = render(
+ ,
+ )
+
+ const textarea = container.querySelector("textarea")!
+ // Ensure cursor is at the beginning
+ textarea.setSelectionRange(0, 0)
+
+ // Navigate to history (saves "My original input" as tempInput)
+ fireEvent.keyDown(textarea, { key: "ArrowUp" })
+ expect(setInputValue).toHaveBeenCalledWith("Third prompt")
+ setInputValue.mockClear()
+
+ // Type something to modify the historical prompt
+ fireEvent.change(textarea, { target: { value: "Modified third prompt", selectionStart: 21 } })
+ expect(setInputValue).toHaveBeenCalledWith("Modified third prompt")
+ setInputValue.mockClear()
+
+ // Now press down arrow - should go back to the first history item
+ textarea.setSelectionRange(0, 0) // Cursor at beginning
+ fireEvent.keyDown(textarea, { key: "ArrowDown" })
+
+ // After typing, pressing down goes back to the first history item
+ expect(setInputValue).toHaveBeenCalledWith("Third prompt")
+ setInputValue.mockClear()
+
+ // Navigate down to return to original input
+ fireEvent.keyDown(textarea, { key: "ArrowDown" })
+ expect(setInputValue).toHaveBeenCalledWith("My original input")
+ })
+
+ it("should preserve original input when modifying history entry and navigating back", () => {
+ const setInputValue = vi.fn()
+ const { container } = render(
+ ,
+ )
+
+ const textarea = container.querySelector("textarea")!
+ // Ensure cursor is at the beginning
+ textarea.setSelectionRange(0, 0)
+
+ // Navigate to history (saves "test" as tempInput)
+ fireEvent.keyDown(textarea, { key: "ArrowUp" })
+ expect(setInputValue).toHaveBeenCalledWith("Third prompt")
+ setInputValue.mockClear()
+
+ // Modify the historical prompt
+ fireEvent.change(textarea, { target: { value: "1Third prompt", selectionStart: 13 } })
+ expect(setInputValue).toHaveBeenCalledWith("1Third prompt")
+ setInputValue.mockClear()
+
+ // Press down arrow - should go back to unmodified history entry (first history item)
+ textarea.setSelectionRange(0, 0) // Cursor at beginning
+ fireEvent.keyDown(textarea, { key: "ArrowDown" })
+ expect(setInputValue).toHaveBeenCalledWith("Third prompt")
+ setInputValue.mockClear()
+
+ // Press down arrow again - should return to original input "test"
+ fireEvent.keyDown(textarea, { key: "ArrowDown" })
+ expect(setInputValue).toHaveBeenCalledWith("test")
+ })
+
it("should reset history navigation when sending message", () => {
const onSend = vi.fn()
const setInputValue = vi.fn()
diff --git a/webview-ui/src/components/chat/hooks/usePromptHistory.ts b/webview-ui/src/components/chat/hooks/usePromptHistory.ts
index 402538182a..265b3af5a3 100644
--- a/webview-ui/src/components/chat/hooks/usePromptHistory.ts
+++ b/webview-ui/src/components/chat/hooks/usePromptHistory.ts
@@ -74,14 +74,16 @@ export const usePromptHistory = ({
setPromptHistory(filteredPromptHistory)
// Reset navigation state when switching between history sources
setHistoryIndex(-1)
+ // Clear tempInput when history source changes (e.g., switching tasks)
setTempInput("")
}, [filteredPromptHistory])
// Reset history navigation when user types (but not when we're setting it programmatically)
const resetOnInputChange = useCallback(() => {
if (historyIndex !== -1) {
+ // Don't clear tempInput - preserve it so user can navigate back to their original input
setHistoryIndex(-1)
- setTempInput("")
+ // Keep tempInput as is, don't clear it
}
}, [historyIndex])
@@ -151,15 +153,30 @@ export const usePromptHistory = ({
return navigateToHistory(historyIndex + 1, textarea, "start")
}
- // Handle DOWN arrow - only in history navigation mode
- if (event.key === "ArrowDown" && historyIndex >= 0 && (isAtBeginning || isAtEnd)) {
+ // Handle DOWN arrow - allow navigation back even after typing
+ if (event.key === "ArrowDown" && (isAtBeginning || isAtEnd)) {
event.preventDefault()
- if (historyIndex > 0) {
- // Keep cursor position consistent with where we started
- return navigateToHistory(historyIndex - 1, textarea, isAtBeginning ? "start" : "end")
- } else if (historyIndex === 0) {
- returnToCurrentInput(textarea, isAtBeginning ? "start" : "end")
+ // If we're not in history mode but have tempInput, it means user typed after navigating
+ // Allow them to go back to history
+ if (historyIndex === -1 && tempInput !== "") {
+ // User typed something after navigating history, but wants to go back
+ // Don't overwrite tempInput - it contains the original input we want to preserve
+ // Go to the most recent history item (index 0)
+ return navigateToHistory(0, textarea, isAtBeginning ? "start" : "end")
+ } else if (historyIndex >= 0) {
+ if (historyIndex > 0) {
+ // Keep cursor position consistent with where we started
+ return navigateToHistory(historyIndex - 1, textarea, isAtBeginning ? "start" : "end")
+ } else if (historyIndex === 0) {
+ // At the most recent history item, go back to original input
+ returnToCurrentInput(textarea, isAtBeginning ? "start" : "end")
+ return true
+ }
+ } else if (historyIndex === -1 && inputValue !== "") {
+ // User is at their original input (or any input) and wants to clear it
+ setInputValue("")
+ setCursorPosition(textarea, isAtBeginning ? "start" : "end", 0)
return true
}
}
@@ -167,11 +184,12 @@ export const usePromptHistory = ({
}
return false
},
- [promptHistory, historyIndex, inputValue, navigateToHistory, returnToCurrentInput],
+ [promptHistory, historyIndex, inputValue, tempInput, navigateToHistory, returnToCurrentInput],
)
const resetHistoryNavigation = useCallback(() => {
setHistoryIndex(-1)
+ // Clear tempInput when explicitly resetting (e.g., when sending a message)
setTempInput("")
}, [])