diff --git a/packages/ui/src/components/prompt-input.tsx b/packages/ui/src/components/prompt-input.tsx
index f15ac22f..351cbbbe 100644
--- a/packages/ui/src/components/prompt-input.tsx
+++ b/packages/ui/src/components/prompt-input.tsx
@@ -519,31 +519,19 @@ export default function PromptInput(props: PromptInputProps) {
return
}
- const atStart = textarea.selectionStart === 0 && textarea.selectionEnd === 0
- const currentHistory = history()
-
- if (e.key === "ArrowUp" && !showPicker() && atStart && currentHistory.length > 0) {
- e.preventDefault()
- if (historyIndex() === -1) {
- setHistoryDraft(prompt())
+ if (e.key === "ArrowUp") {
+ const handled = selectPreviousHistory()
+ if (handled) {
+ e.preventDefault()
+ return
}
- const newIndex = historyIndex() === -1 ? 0 : Math.min(historyIndex() + 1, currentHistory.length - 1)
- setHistoryIndex(newIndex)
- setPrompt(currentHistory[newIndex])
- return
}
- if (e.key === "ArrowDown" && !showPicker() && historyIndex() >= 0) {
- e.preventDefault()
- const newIndex = historyIndex() - 1
- if (newIndex >= 0) {
- setHistoryIndex(newIndex)
- setPrompt(currentHistory[newIndex])
- } else {
- setHistoryIndex(-1)
- const draft = historyDraft()
- setPrompt(draft ?? "")
- setHistoryDraft(null)
+ if (e.key === "ArrowDown") {
+ const handled = selectNextHistory()
+ if (handled) {
+ e.preventDefault()
+ return
}
}
}
@@ -602,6 +590,60 @@ export default function PromptInput(props: PromptInputProps) {
}
}
+ function focusTextareaEnd() {
+ if (!textareaRef) return
+ setTimeout(() => {
+ if (!textareaRef) return
+ const pos = textareaRef.value.length
+ textareaRef.setSelectionRange(pos, pos)
+ textareaRef.focus()
+ }, 0)
+ }
+
+ function canUseHistory(force = false) {
+ if (force) return true
+ if (showPicker()) return false
+ const textarea = textareaRef
+ if (!textarea) return false
+ return textarea.selectionStart === 0 && textarea.selectionEnd === 0
+ }
+
+ function selectPreviousHistory(force = false) {
+ const entries = history()
+ if (entries.length === 0) return false
+ if (!canUseHistory(force)) return false
+
+ if (historyIndex() === -1) {
+ setHistoryDraft(prompt())
+ }
+
+ const newIndex = historyIndex() === -1 ? 0 : Math.min(historyIndex() + 1, entries.length - 1)
+ setHistoryIndex(newIndex)
+ setPrompt(entries[newIndex])
+ focusTextareaEnd()
+ return true
+ }
+
+ function selectNextHistory(force = false) {
+ const entries = history()
+ if (entries.length === 0) return false
+ if (!canUseHistory(force)) return false
+ if (historyIndex() === -1) return false
+
+ const newIndex = historyIndex() - 1
+ if (newIndex >= 0) {
+ setHistoryIndex(newIndex)
+ setPrompt(entries[newIndex])
+ } else {
+ setHistoryIndex(-1)
+ const draft = historyDraft()
+ setPrompt(draft ?? "")
+ setHistoryDraft(null)
+ }
+ focusTextareaEnd()
+ return true
+ }
+
function handleAbort() {
if (!props.onAbortSession || !props.isSessionBusy) return
void props.onAbortSession()
@@ -828,6 +870,10 @@ export default function PromptInput(props: PromptInputProps) {
const canStop = () => Boolean(props.isSessionBusy && props.onAbortSession)
+ const hasHistory = () => history().length > 0
+ const canHistoryGoPrevious = () => hasHistory() && (historyIndex() === -1 || historyIndex() < history().length - 1)
+ const canHistoryGoNext = () => historyIndex() >= 0
+
const canSend = () => {
if (props.disabled) return false
const hasText = prompt().trim().length > 0
@@ -986,6 +1032,34 @@ export default function PromptInput(props: PromptInputProps) {
autoCapitalize="off"
autocomplete="off"
/>
+
+
+
+
+
+
+
+