diff --git a/packages/ui/src/views/chatmessage/ChatInputHistory.js b/packages/ui/src/views/chatmessage/ChatInputHistory.js new file mode 100644 index 00000000000..05cd7c513de --- /dev/null +++ b/packages/ui/src/views/chatmessage/ChatInputHistory.js @@ -0,0 +1,62 @@ +export class ChatInputHistory { + constructor(maxHistory = 10) { + this.history = [] + this.currentIndex = -1 + this.tempInput = '' + this.maxHistory = maxHistory + this.loadHistory() + } + + addToHistory(input) { + if (!input.trim()) return + if (this.history[0] !== input) { + this.history.unshift(input) + if (this.history.length > this.maxHistory) { + this.history.pop() + } + } + this.currentIndex = -1 + this.saveHistory() + } + + getPreviousInput(currentInput) { + if (this.currentIndex === -1) { + this.tempInput = currentInput + } + if (this.currentIndex < this.history.length - 1) { + this.currentIndex++ + return this.history[this.currentIndex] + } + return this.history[this.currentIndex] || this.tempInput + } + + getNextInput() { + if (this.currentIndex > -1) { + this.currentIndex-- + if (this.currentIndex === -1) { + return this.tempInput + } + return this.history[this.currentIndex] + } + return this.tempInput + } + + saveHistory() { + try { + localStorage.setItem('chatInputHistory', JSON.stringify(this.history)) + } catch (error) { + console.warn('Failed to save chat history to localStorage:', error) + } + } + + loadHistory() { + try { + const saved = localStorage.getItem('chatInputHistory') + if (saved) { + this.history = JSON.parse(saved) + } + } catch (error) { + console.warn('Failed to load chat history from localStorage:', error) + } + } +} diff --git a/packages/ui/src/views/chatmessage/ChatMessage.jsx b/packages/ui/src/views/chatmessage/ChatMessage.jsx index 96d722b3b1f..c7ae695fa6e 100644 --- a/packages/ui/src/views/chatmessage/ChatMessage.jsx +++ b/packages/ui/src/views/chatmessage/ChatMessage.jsx @@ -83,6 +83,9 @@ import { isValidURL, removeDuplicateURL, setLocalStorageChatflow, getLocalStorag import useNotifier from '@/utils/useNotifier' import FollowUpPromptsCard from '@/ui-component/cards/FollowUpPromptsCard' +// History +import { ChatInputHistory } from './ChatInputHistory' + const messageImageStyle = { width: '128px', height: '128px', @@ -185,6 +188,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview const [uploadedFiles, setUploadedFiles] = useState([]) const [imageUploadAllowedTypes, setImageUploadAllowedTypes] = useState('') const [fileUploadAllowedTypes, setFileUploadAllowedTypes] = useState('') + const [inputHistory] = useState(new ChatInputHistory(10)) const inputRef = useRef(null) const getChatmessageApi = useApi(chatmessageApi.getInternalChatmessageFromChatflow) @@ -768,6 +772,10 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview if (selectedInput !== undefined && selectedInput.trim() !== '') input = selectedInput + if (input.trim()) { + inputHistory.addToHistory(input) + } + setLoading(true) let uploads = previews.map((item) => { return { @@ -934,7 +942,15 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview const handleEnter = (e) => { // Check if IME composition is in progress const isIMEComposition = e.isComposing || e.keyCode === 229 - if (e.key === 'Enter' && userInput && !isIMEComposition) { + if (e.key === 'ArrowUp' && !isIMEComposition) { + e.preventDefault() + const previousInput = inputHistory.getPreviousInput(userInput) + setUserInput(previousInput) + } else if (e.key === 'ArrowDown' && !isIMEComposition) { + e.preventDefault() + const nextInput = inputHistory.getNextInput() + setUserInput(nextInput) + } else if (e.key === 'Enter' && userInput && !isIMEComposition) { if (!e.shiftKey && userInput) { handleSubmit(e) }