From e9b8733bb93f23a19b8607f8572f44e0af0bcdb0 Mon Sep 17 00:00:00 2001 From: Tyler Merritt Date: Fri, 1 Nov 2024 18:06:42 -0500 Subject: [PATCH 1/3] Add Up and Down arrow functionality to chat messages - Works like Linux shell - History limited to 10 messages --- packages/ui/src/views/chatflows/EmbedChat.jsx | 18 ++++++ .../src/views/chatmessage/ChatInputHistory.js | 62 +++++++++++++++++++ .../ui/src/views/chatmessage/ChatMessage.jsx | 38 ++++++++---- 3 files changed, 107 insertions(+), 11 deletions(-) create mode 100644 packages/ui/src/views/chatmessage/ChatInputHistory.js diff --git a/packages/ui/src/views/chatflows/EmbedChat.jsx b/packages/ui/src/views/chatflows/EmbedChat.jsx index 03ddbe24c33..c6ceaf5f2a5 100644 --- a/packages/ui/src/views/chatflows/EmbedChat.jsx +++ b/packages/ui/src/views/chatflows/EmbedChat.jsx @@ -173,6 +173,15 @@ const chatwindowConfig = (isReact = false) => { // sendSoundLocation: "send_message.mp3", // If this is not used, the default sound effect will be played if sendSoundMessage is true. receiveMessageSound: true, // receiveSoundLocation: "receive_message.mp3", // If this is not used, the default sound effect will be played if receiveSoundMessage is true. + inputHistory: { + enabled: true, + maxHistory: 10, // Store last 10 messages + persistHistory: true, // Optional: save to localStorage + }, + keyBindings: { + upArrow: 'previousInput', + downArrow: 'nextInput' + }, }, feedback: { color: '#303235', @@ -223,6 +232,15 @@ const chatwindowConfig = (isReact = false) => { // sendSoundLocation: "send_message.mp3", // If this is not used, the default sound effect will be played if sendSoundMessage is true. receiveMessageSound: true, // receiveSoundLocation: "receive_message.mp3", // If this is not used, the default sound effect will be played if receiveSoundMessage is true. + inputHistory: { + enabled: true, + maxHistory: 10, // Store last 10 messages + persistHistory: true, // Optional: save to localStorage + }, + keyBindings: { + upArrow: 'previousInput', + downArrow: 'nextInput' + }, }, feedback: { color: '#303235', 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..adb5f0d679e 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) } @@ -1540,8 +1556,8 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview ? 'usermessagewaiting-dark' : 'usermessagewaiting-light' : message.type === 'usermessagewaiting' - ? 'apimessage' - : 'usermessage' + ? 'apimessage' + : 'usermessage' } > {/* Display the correct icon depending on the message type */} @@ -1817,8 +1833,8 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview )}
{message.type === 'leadCaptureMessage' && - !getLocalStorageChatflow(chatflowid)?.lead && - leadsConfig.status ? ( + !getLocalStorageChatflow(chatflowid)?.lead && + leadsConfig.status ? ( copyMessageToClipboard(message.message)} /> {!message.feedback || - message.feedback.rating === '' || - message.feedback.rating === 'THUMBS_UP' ? ( + message.feedback.rating === '' || + message.feedback.rating === 'THUMBS_UP' ? ( ) : null} {!message.feedback || - message.feedback.rating === '' || - message.feedback.rating === 'THUMBS_DOWN' ? ( + message.feedback.rating === '' || + message.feedback.rating === 'THUMBS_DOWN' ? ( From b97f04ed8c4d4612d26fabf3ef23b9ba48b894fb Mon Sep 17 00:00:00 2001 From: Tyler Merritt Date: Mon, 4 Nov 2024 13:03:36 -0600 Subject: [PATCH 2/3] Fix linting errors --- .../ui/src/views/chatmessage/ChatMessage.jsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/ui/src/views/chatmessage/ChatMessage.jsx b/packages/ui/src/views/chatmessage/ChatMessage.jsx index adb5f0d679e..c7ae695fa6e 100644 --- a/packages/ui/src/views/chatmessage/ChatMessage.jsx +++ b/packages/ui/src/views/chatmessage/ChatMessage.jsx @@ -1556,8 +1556,8 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview ? 'usermessagewaiting-dark' : 'usermessagewaiting-light' : message.type === 'usermessagewaiting' - ? 'apimessage' - : 'usermessage' + ? 'apimessage' + : 'usermessage' } > {/* Display the correct icon depending on the message type */} @@ -1833,8 +1833,8 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview )}
{message.type === 'leadCaptureMessage' && - !getLocalStorageChatflow(chatflowid)?.lead && - leadsConfig.status ? ( + !getLocalStorageChatflow(chatflowid)?.lead && + leadsConfig.status ? ( copyMessageToClipboard(message.message)} /> {!message.feedback || - message.feedback.rating === '' || - message.feedback.rating === 'THUMBS_UP' ? ( + message.feedback.rating === '' || + message.feedback.rating === 'THUMBS_UP' ? ( ) : null} {!message.feedback || - message.feedback.rating === '' || - message.feedback.rating === 'THUMBS_DOWN' ? ( + message.feedback.rating === '' || + message.feedback.rating === 'THUMBS_DOWN' ? ( From f94137dc19f3572cfede0da4d36e5dd743b83b09 Mon Sep 17 00:00:00 2001 From: Henry Heng Date: Sat, 9 Nov 2024 00:01:33 +0000 Subject: [PATCH 3/3] Update EmbedChat.jsx --- packages/ui/src/views/chatflows/EmbedChat.jsx | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/ui/src/views/chatflows/EmbedChat.jsx b/packages/ui/src/views/chatflows/EmbedChat.jsx index c6ceaf5f2a5..03ddbe24c33 100644 --- a/packages/ui/src/views/chatflows/EmbedChat.jsx +++ b/packages/ui/src/views/chatflows/EmbedChat.jsx @@ -173,15 +173,6 @@ const chatwindowConfig = (isReact = false) => { // sendSoundLocation: "send_message.mp3", // If this is not used, the default sound effect will be played if sendSoundMessage is true. receiveMessageSound: true, // receiveSoundLocation: "receive_message.mp3", // If this is not used, the default sound effect will be played if receiveSoundMessage is true. - inputHistory: { - enabled: true, - maxHistory: 10, // Store last 10 messages - persistHistory: true, // Optional: save to localStorage - }, - keyBindings: { - upArrow: 'previousInput', - downArrow: 'nextInput' - }, }, feedback: { color: '#303235', @@ -232,15 +223,6 @@ const chatwindowConfig = (isReact = false) => { // sendSoundLocation: "send_message.mp3", // If this is not used, the default sound effect will be played if sendSoundMessage is true. receiveMessageSound: true, // receiveSoundLocation: "receive_message.mp3", // If this is not used, the default sound effect will be played if receiveSoundMessage is true. - inputHistory: { - enabled: true, - maxHistory: 10, // Store last 10 messages - persistHistory: true, // Optional: save to localStorage - }, - keyBindings: { - upArrow: 'previousInput', - downArrow: 'nextInput' - }, }, feedback: { color: '#303235',