diff --git a/component/src/deepChat.ts b/component/src/deepChat.ts index 7fed1c099..3d4c55877 100644 --- a/component/src/deepChat.ts +++ b/component/src/deepChat.ts @@ -154,7 +154,7 @@ export class DeepChat extends InternalHTML { clearMessages: (isReset?: boolean) => void = () => {}; - changeMessage: (index: number, messageBody: MessageBody) => void = () => {}; + updateMessage: (index: number, messageBody: MessageBody) => void = () => {}; scrollToBottom: () => void = () => {}; diff --git a/component/src/services/gemini/gemini.ts b/component/src/services/gemini/gemini.ts index 18c502558..922fce10b 100644 --- a/component/src/services/gemini/gemini.ts +++ b/component/src/services/gemini/gemini.ts @@ -1,7 +1,7 @@ import {OpenAIConverseResult, ResultChoice, ToolAPI, ToolCalls} from '../../types/openAIResult'; import {OpenAIConverseBodyInternal, SystemMessageInternal} from '../../types/openAIInternal'; +import {MessageUtils} from '../../views/chat/messages/utils/messageUtils'; import {FetchFunc, RequestUtils} from '../../utils/HTTP/requestUtils'; -import {MessageUtils} from '../../views/chat/messages/messageUtils'; import {ChatFunctionHandler, OpenAIChat} from '../../types/openAI'; import {DirectConnection} from '../../types/directConnection'; import {MessageLimitUtils} from '../utils/messageLimitUtils'; diff --git a/component/src/services/openAI/assistant/openAIAssistantIOI.ts b/component/src/services/openAI/assistant/openAIAssistantIOI.ts index 136dad6e0..1898933d3 100644 --- a/component/src/services/openAI/assistant/openAIAssistantIOI.ts +++ b/component/src/services/openAI/assistant/openAIAssistantIOI.ts @@ -1,8 +1,8 @@ import {AssistantFunctionHandler, OpenAI, OpenAIAssistant, OpenAINewAssistant} from '../../../types/openAI'; +import {FileMessageUtils} from '../../../views/chat/messages/utils/fileMessageUtils'; import {MessageContentI, MessageToElements} from '../../../types/messagesInternal'; import {OpenAIAssistantUtils, UploadedFile} from './utils/openAIAssistantUtils'; import {MessageStream} from '../../../views/chat/messages/stream/messageStream'; -import {FileMessageUtils} from '../../../views/chat/messages/fileMessageUtils'; import {KeyVerificationDetails} from '../../../types/keyVerificationDetails'; import {OpenAIConverseBodyInternal} from '../../../types/openAIInternal'; import {History} from '../../../views/chat/messages/history/history'; diff --git a/component/src/services/openAI/openAIChatIO.ts b/component/src/services/openAI/openAIChatIO.ts index fa38766d0..8f653d688 100644 --- a/component/src/services/openAI/openAIChatIO.ts +++ b/component/src/services/openAI/openAIChatIO.ts @@ -1,8 +1,8 @@ import {OpenAIConverseResult, ResultChoice, ToolAPI, ToolCalls} from '../../types/openAIResult'; import {OpenAIConverseBodyInternal, SystemMessageInternal} from '../../types/openAIInternal'; import {KeyVerificationDetails} from '../../types/keyVerificationDetails'; +import {MessageUtils} from '../../views/chat/messages/utils/messageUtils'; import {FetchFunc, RequestUtils} from '../../utils/HTTP/requestUtils'; -import {MessageUtils} from '../../views/chat/messages/messageUtils'; import {ChatFunctionHandler, OpenAIChat} from '../../types/openAI'; import {DirectConnection} from '../../types/directConnection'; import {MessageLimitUtils} from '../utils/messageLimitUtils'; diff --git a/component/src/utils/HTTP/customHandler.ts b/component/src/utils/HTTP/customHandler.ts index 447600817..9e1a5f859 100644 --- a/component/src/utils/HTTP/customHandler.ts +++ b/component/src/utils/HTTP/customHandler.ts @@ -1,5 +1,5 @@ import {MessageStream} from '../../views/chat/messages/stream/messageStream'; -import {MessageUtils} from '../../views/chat/messages/messageUtils'; +import {MessageUtils} from '../../views/chat/messages/utils/messageUtils'; import {MessagesBase} from '../../views/chat/messages/messagesBase'; import {ErrorMessages} from '../errorMessages/errorMessages'; import {Messages} from '../../views/chat/messages/messages'; diff --git a/component/src/utils/HTTP/websocket.ts b/component/src/utils/HTTP/websocket.ts index f6c309760..62ff356a7 100644 --- a/component/src/utils/HTTP/websocket.ts +++ b/component/src/utils/HTTP/websocket.ts @@ -1,5 +1,5 @@ import {MessageStream} from '../../views/chat/messages/stream/messageStream'; -import {MessageUtils} from '../../views/chat/messages/messageUtils'; +import {MessageUtils} from '../../views/chat/messages/utils/messageUtils'; import {CustomHandler, IWebsocketHandler} from './customHandler'; import {ErrorMessages} from '../errorMessages/errorMessages'; import {Messages} from '../../views/chat/messages/messages'; diff --git a/component/src/utils/events/fireEvents.ts b/component/src/utils/events/fireEvents.ts index f6c648e05..d0f11a6dc 100644 --- a/component/src/utils/events/fireEvents.ts +++ b/component/src/utils/events/fireEvents.ts @@ -1,4 +1,4 @@ -import {FileMessageUtils} from '../../views/chat/messages/fileMessageUtils'; +import {FileMessageUtils} from '../../views/chat/messages/utils/fileMessageUtils'; import {MessageContentI} from '../../types/messagesInternal'; import {DeepChat} from '../../deepChat'; import {Legacy} from '../legacy/legacy'; diff --git a/component/src/views/chat/input/buttons/submit/submitButton.ts b/component/src/views/chat/input/buttons/submit/submitButton.ts index d9647d2bd..0a0e4f444 100644 --- a/component/src/views/chat/input/buttons/submit/submitButton.ts +++ b/component/src/views/chat/input/buttons/submit/submitButton.ts @@ -7,10 +7,10 @@ import {SpeechToText} from '../microphone/speechToText/speechToText'; import {SUBMIT_ICON_STRING} from '../../../../../icons/submitIcon'; import {SVGIconUtils} from '../../../../../utils/svg/svgIconUtils'; import {UserContentI} from '../../../../../types/messagesInternal'; +import {MessageUtils} from '../../../messages/utils/messageUtils'; import {SubmitButtonStateStyle} from './submitButtonStateStyle'; import {MicrophoneButton} from '../microphone/microphoneButton'; import {ServiceIO} from '../../../../../services/serviceIO'; -import {MessageUtils} from '../../../messages/messageUtils'; import {UserContent} from '../../../../../types/messages'; import {Legacy} from '../../../../../utils/legacy/legacy'; import {Response} from '../../../../../types/response'; diff --git a/component/src/views/chat/messages/avatar.ts b/component/src/views/chat/messages/avatar.ts index ae74bee6b..c68764df3 100644 --- a/component/src/views/chat/messages/avatar.ts +++ b/component/src/views/chat/messages/avatar.ts @@ -1,7 +1,7 @@ import {Avatars, AvatarStyles, CustomAvatars} from '../../../types/avatars'; import aiLogoUrl from '../../../../assets/machine-learning.svg'; import avatarUrl from '../../../../assets/person-avatar.png'; -import {MessageUtils} from './messageUtils'; +import {MessageUtils} from './utils/messageUtils'; export class Avatar { private static readonly CONTAINER_CLASS = 'avatar-container'; diff --git a/component/src/views/chat/messages/fileMessages.ts b/component/src/views/chat/messages/fileMessages.ts index 20660edb3..14b2d6c90 100644 --- a/component/src/views/chat/messages/fileMessages.ts +++ b/component/src/views/chat/messages/fileMessages.ts @@ -1,10 +1,10 @@ import {MessageFile, MessageFiles} from '../../../types/messageFile'; import {SVGIconUtils} from '../../../utils/svg/svgIconUtils'; +import {FileMessageUtils} from './utils/fileMessageUtils'; import {FILE_ICON_STRING} from '../../../icons/fileIcon'; import {Browser} from '../../../utils/browser/browser'; import {MessageStyles} from '../../../types/messages'; -import {FileMessageUtils} from './fileMessageUtils'; -import {MessageUtils} from './messageUtils'; +import {MessageUtils} from './utils/messageUtils'; import {MessagesBase} from './messagesBase'; export class FileMessages { diff --git a/component/src/views/chat/messages/history/history.ts b/component/src/views/chat/messages/history/history.ts index aa05d9249..7f99171a0 100644 --- a/component/src/views/chat/messages/history/history.ts +++ b/component/src/views/chat/messages/history/history.ts @@ -4,9 +4,9 @@ import {MessageContentI} from '../../../../types/messagesInternal'; import {MessageContent} from '../../../../types/messages'; import {ServiceIO} from '../../../../services/serviceIO'; import {Legacy} from '../../../../utils/legacy/legacy'; +import {MessageUtils} from '../utils/messageUtils'; import {LoadingHistory} from './loadingHistory'; import {DeepChat} from '../../../../deepChat'; -import {MessageUtils} from '../messageUtils'; import {Messages} from '../messages'; export class History { diff --git a/component/src/views/chat/messages/history/loadingHistory.ts b/component/src/views/chat/messages/history/loadingHistory.ts index 5414fecc6..df50e5484 100644 --- a/component/src/views/chat/messages/history/loadingHistory.ts +++ b/component/src/views/chat/messages/history/loadingHistory.ts @@ -1,9 +1,9 @@ import {LoadingStyle} from '../../../../utils/loading/loadingStyle'; import {MessageElementsStyles} from '../../../../types/messages'; import {MessageElements, Messages} from '../messages'; +import {MessageUtils} from '../utils/messageUtils'; import {HTMLMessages} from '../html/htmlMessages'; import {MessagesBase} from '../messagesBase'; -import {MessageUtils} from '../messageUtils'; export class LoadingHistory { public static readonly CLASS = 'loading-history-message'; diff --git a/component/src/views/chat/messages/html/htmlMessages.ts b/component/src/views/chat/messages/html/htmlMessages.ts index c5b8f0101..9fe6cb3cb 100644 --- a/component/src/views/chat/messages/html/htmlMessages.ts +++ b/component/src/views/chat/messages/html/htmlMessages.ts @@ -1,6 +1,6 @@ import {Overwrite} from '../../../../types/messagesInternal'; import {Legacy} from '../../../../utils/legacy/legacy'; -import {MessageUtils} from '../messageUtils'; +import {MessageUtils} from '../utils/messageUtils'; import {MessagesBase} from '../messagesBase'; import {MessageElements} from '../messages'; import {HTMLUtils} from './htmlUtils'; diff --git a/component/src/views/chat/messages/messages.ts b/component/src/views/chat/messages/messages.ts index 108b39279..33e847453 100644 --- a/component/src/views/chat/messages/messages.ts +++ b/component/src/views/chat/messages/messages.ts @@ -5,23 +5,23 @@ import {LoadingStyle} from '../../../utils/loading/loadingStyle'; import {HTMLDeepChatElements} from './html/htmlDeepChatElements'; import {ElementUtils} from '../../../utils/element/elementUtils'; import {FireEvents} from '../../../utils/events/fireEvents'; +import {MessageStyleUtils} from './utils/messageStyleUtils'; import {ErrorMessageOverrides} from '../../../types/error'; import {ResponseI} from '../../../types/responseInternal'; +import {FileMessageUtils} from './utils/fileMessageUtils'; import {TextToSpeech} from './textToSpeech/textToSpeech'; import {LoadingHistory} from './history/loadingHistory'; import {ErrorResp} from '../../../types/errorInternal'; import {Demo, DemoResponse} from '../../../types/demo'; -import {MessageStyleUtils} from './messageStyleUtils'; import {IntroMessage} from '../../../types/messages'; import {MessageStream} from './stream/messageStream'; import {IntroPanel} from '../introPanel/introPanel'; -import {FileMessageUtils} from './fileMessageUtils'; import {WebModel} from '../../../webModel/webModel'; import {CustomStyle} from '../../../types/styles'; +import {MessageUtils} from './utils/messageUtils'; import {HTMLMessages} from './html/htmlMessages'; import {SetupMessages} from './setupMessages'; import {FileMessages} from './fileMessages'; -import {MessageUtils} from './messageUtils'; import {MessagesBase} from './messagesBase'; import {DeepChat} from '../../../deepChat'; import {HTMLUtils} from './html/htmlUtils'; diff --git a/component/src/views/chat/messages/messagesBase.ts b/component/src/views/chat/messages/messagesBase.ts index a32fece49..dae63b33a 100644 --- a/component/src/views/chat/messages/messagesBase.ts +++ b/component/src/views/chat/messages/messagesBase.ts @@ -5,15 +5,16 @@ import {ElementUtils} from '../../../utils/element/elementUtils'; import {HTMLDeepChatElements} from './html/htmlDeepChatElements'; import {LoadingStyle} from '../../../utils/loading/loadingStyle'; import {RemarkableConfig} from './remarkable/remarkableConfig'; +import {MessageStyleUtils} from './utils/messageStyleUtils'; import {FireEvents} from '../../../utils/events/fireEvents'; import {LoadingHistory} from './history/loadingHistory'; import {HTMLClassUtilities} from '../../../types/html'; -import {MessageStyleUtils} from './messageStyleUtils'; import {IntroPanel} from '../introPanel/introPanel'; import {Legacy} from '../../../utils/legacy/legacy'; +import {UpdateMessage} from './utils/updateMessage'; +import {MessageUtils} from './utils/messageUtils'; import {Response} from '../../../types/response'; import {Avatars} from '../../../types/avatars'; -import {MessageUtils} from './messageUtils'; import {DeepChat} from '../../../deepChat'; import {Names} from '../../../types/names'; import {MessageElements} from './messages'; @@ -42,7 +43,7 @@ export class MessagesBase { this._names = deepChat.names; this._onMessage = FireEvents.onMessage.bind(this, deepChat); if (deepChat.htmlClassUtilities) this.htmlClassUtilities = deepChat.htmlClassUtilities; - deepChat.changeMessage = this.changeMessage.bind(this); + deepChat.updateMessage = (index: number, messageBody: MessageBody) => UpdateMessage.update(this, index, messageBody); setTimeout(() => { this.submitUserMessage = deepChat.submitUserMessage; // wait for it to be available in input.ts }); @@ -230,9 +231,4 @@ export class MessagesBase { if (msgToEls[1].text && msgToEls[0].text) this.renderText(msgToEls[1].text.bubbleElement, msgToEls[0].text); }); } - - private changeMessage(index: number, messageBody: MessageBody) { - const messageToEls = this.messageToElements[index]; - MessageUtils.changeMessage(this, messageToEls, messageBody); - } } diff --git a/component/src/views/chat/messages/name.ts b/component/src/views/chat/messages/name.ts index f6e9fa668..ed065e2a3 100644 --- a/component/src/views/chat/messages/name.ts +++ b/component/src/views/chat/messages/name.ts @@ -1,5 +1,5 @@ import {Names, Name as NameT, CustomNames} from '../../../types/names'; -import {MessageUtils} from './messageUtils'; +import {MessageUtils} from './utils/messageUtils'; export class Name { private static readonly NAME_CLASS = 'name'; diff --git a/component/src/views/chat/messages/stream/messageStream.ts b/component/src/views/chat/messages/stream/messageStream.ts index 7c09e27ac..151c3945f 100644 --- a/component/src/views/chat/messages/stream/messageStream.ts +++ b/component/src/views/chat/messages/stream/messageStream.ts @@ -5,8 +5,8 @@ import {TextToSpeech} from '../textToSpeech/textToSpeech'; import {MessageFile} from '../../../../types/messageFile'; import {MessageElements, Messages} from '../messages'; import {Response} from '../../../../types/response'; +import {MessageUtils} from '../utils/messageUtils'; import {HTMLMessages} from '../html/htmlMessages'; -import {MessageUtils} from '../messageUtils'; import {MessagesBase} from '../messagesBase'; import {HTMLUtils} from '../html/htmlUtils'; diff --git a/component/src/views/chat/messages/fileMessageUtils.ts b/component/src/views/chat/messages/utils/fileMessageUtils.ts similarity index 95% rename from component/src/views/chat/messages/fileMessageUtils.ts rename to component/src/views/chat/messages/utils/fileMessageUtils.ts index 60ab411d3..27c10e38e 100644 --- a/component/src/views/chat/messages/fileMessageUtils.ts +++ b/component/src/views/chat/messages/utils/fileMessageUtils.ts @@ -1,7 +1,7 @@ -import {MessageFile, MessageFileType} from '../../../types/messageFile'; -import {MessageContent, MessageStyles} from '../../../types/messages'; -import {MessagesBase} from './messagesBase'; -import {MessageElements} from './messages'; +import {MessageFile, MessageFileType} from '../../../../types/messageFile'; +import {MessageContent, MessageStyles} from '../../../../types/messages'; +import {MessagesBase} from '../messagesBase'; +import {MessageElements} from '../messages'; export class FileMessageUtils { public static readonly DEFAULT_FILE_NAME = 'file'; diff --git a/component/src/views/chat/messages/messageStyleUtils.ts b/component/src/views/chat/messages/utils/messageStyleUtils.ts similarity index 93% rename from component/src/views/chat/messages/messageStyleUtils.ts rename to component/src/views/chat/messages/utils/messageStyleUtils.ts index fa52ea053..80f81cd22 100644 --- a/component/src/views/chat/messages/messageStyleUtils.ts +++ b/component/src/views/chat/messages/utils/messageStyleUtils.ts @@ -1,9 +1,9 @@ -import {MessageElementsStyles, MessageRoleStyles, MessageStyles} from '../../../types/messages'; -import {OverrideTypes} from '../../../types/utilityTypes'; -import {GenericObject} from '../../../types/object'; -import {CustomStyle} from '../../../types/styles'; +import {MessageElementsStyles, MessageRoleStyles, MessageStyles} from '../../../../types/messages'; +import {OverrideTypes} from '../../../../types/utilityTypes'; +import {GenericObject} from '../../../../types/object'; +import {CustomStyle} from '../../../../types/styles'; +import {MessageElements} from '../messages'; import {MessageUtils} from './messageUtils'; -import {MessageElements} from './messages'; export class MessageStyleUtils { public static applyCustomStylesToElements(elements: MessageElements, isMedia: boolean, styles?: MessageElementsStyles) { diff --git a/component/src/views/chat/messages/messageUtils.ts b/component/src/views/chat/messages/utils/messageUtils.ts similarity index 56% rename from component/src/views/chat/messages/messageUtils.ts rename to component/src/views/chat/messages/utils/messageUtils.ts index 5226fa825..f77e93ebb 100644 --- a/component/src/views/chat/messages/messageUtils.ts +++ b/component/src/views/chat/messages/utils/messageUtils.ts @@ -1,17 +1,14 @@ -import {MessageBody, MessageBodyElements, MessageContentI, MessageToElements} from '../../../types/messagesInternal'; -import {MessageContent, MessageStyles} from '../../../types/messages'; -import {LoadingStyle} from '../../../utils/loading/loadingStyle'; -import {MessageFile} from '../../../types/messageFile'; +import {MessageBodyElements, MessageContentI, MessageToElements} from '../../../../types/messagesInternal'; +import {LoadingStyle} from '../../../../utils/loading/loadingStyle'; +import {MessageContent} from '../../../../types/messages'; import {FileMessageUtils} from './fileMessageUtils'; -import {HTMLMessages} from './html/htmlMessages'; -import {Avatars} from '../../../types/avatars'; -import {MessagesBase} from './messagesBase'; -import {FileMessages} from './fileMessages'; -import {MessageElements, Messages} from './messages'; -import {Names} from '../../../types/names'; -import {Avatar} from './avatar'; -import {Name} from './name'; -import {MessageStream} from './stream/messageStream'; +import {HTMLMessages} from '../html/htmlMessages'; +import {Avatars} from '../../../../types/avatars'; +import {Names} from '../../../../types/names'; +import {MessagesBase} from '../messagesBase'; +import {MessageElements} from '../messages'; +import {Avatar} from '../avatar'; +import {Name} from '../name'; export class MessageUtils { public static readonly AI_ROLE = 'ai'; @@ -198,114 +195,4 @@ export class MessageUtils { } }); } - - private static removeElements(messageElementRefs: MessageElements[], elemsToRemove?: MessageElements) { - if (!elemsToRemove) return; - const removalElsIndex = messageElementRefs.findIndex((messageElements) => messageElements === elemsToRemove); - messageElementRefs.splice(removalElsIndex, 1); - elemsToRemove?.outerContainer.remove(); - } - - private static removeFilesMessages(msg: MessagesBase, messageToEls: MessageToElements[0]) { - messageToEls[1].files?.forEach((file) => { - MessageUtils.removeElements(msg.messageElementRefs, file); - }); - delete messageToEls[0].files; - delete messageToEls[1].files; - } - - private static removeTextHTMLMessage(msg: MessagesBase, messageToEls: MessageToElements[0], type: 'text' | 'html') { - const elemsToRemove = messageToEls[1][type]; - MessageUtils.removeElements(msg.messageElementRefs, elemsToRemove); - delete messageToEls[0][type]; - delete messageToEls[1][type]; - } - - private static changeHTMLMessage(msg: MessagesBase, messageToEls: MessageToElements[0], newHTML: string) { - if (messageToEls[1].html) { - HTMLMessages.overwriteElements(msg, newHTML, messageToEls[1].html); - } else { - const messageElements = HTMLMessages.create(msg, newHTML, messageToEls[0].role); - const previousElements = (messageToEls[1].files?.[messageToEls[1].files?.length - 1] || - messageToEls[1].text) as MessageElements; - msg.elementRef.insertBefore(messageElements.outerContainer, previousElements.outerContainer.nextSibling); - msg.messageElementRefs.splice(msg.messageElementRefs.length - 1, 1); // removing as createMessageElements adds one - const prevMsgElsIndex = msg.messageElementRefs.findIndex((messageElements) => messageElements === previousElements); - msg.messageElementRefs.splice(prevMsgElsIndex + 1, 0, messageElements); - messageToEls[1].html = messageElements; - } - messageToEls[0].html = newHTML; - } - - // finds beforeElement, creates new elements, remove old and adds new ones - private static changeFileMessages(msg: MessagesBase, messageToEls: MessageToElements[0], newFiles: MessageFile[]) { - const role = messageToEls[0].role; - const typeToElements = FileMessages.createMessages(msg, newFiles, role); - const nextElement = messageToEls[1].html; - const prevElement = messageToEls[1].files?.[messageToEls[1].files?.length - 1] || messageToEls[1].text; - const siblingElement = (nextElement || prevElement) as MessageElements; - let siblingElementIndex = msg.messageElementRefs.findIndex((messageElements) => messageElements === siblingElement); - if (prevElement) siblingElementIndex += 1; - const beforeElement = (nextElement?.outerContainer || prevElement?.outerContainer.nextSibling) as Node; - typeToElements.forEach(({type, elements}, index) => { - FileMessageUtils.setElementProps(msg, elements, type as keyof MessageStyles, role); - msg.elementRef.insertBefore(elements.outerContainer, beforeElement); - msg.messageElementRefs.splice(msg.messageElementRefs.length - 1, 1); // removing as createMessageElements adds one - msg.messageElementRefs.splice(siblingElementIndex + index, 0, elements); - }); - MessageUtils.removeFilesMessages(msg, messageToEls); - messageToEls[1].files = typeToElements.map(({elements}) => elements); - messageToEls[0].files = newFiles; - } - - private static changeTextMessage(msg: MessagesBase, messageToEls: MessageToElements[0], newText: string) { - if (messageToEls[1].text) { - msg.renderText(messageToEls[1].text.bubbleElement, newText); - } else { - const messageElements = msg.createElements(newText, messageToEls[0].role); - const nextElements = (messageToEls[1].files?.[0] || messageToEls[1].html) as MessageElements; - msg.elementRef.insertBefore(messageElements.outerContainer, nextElements.outerContainer); - const nextMsgElsIndex = msg.messageElementRefs.findIndex((messageElements) => messageElements === nextElements); - msg.messageElementRefs.splice(nextMsgElsIndex, 0, messageElements); - messageToEls[1].text = messageElements; - } - messageToEls[0].text = newText; - } - - private static isElementActive(elements: MessageBodyElements) { - return ( - Messages.isActiveElement(elements.text?.bubbleElement.classList) || - Messages.isActiveElement(elements.html?.bubbleElement.classList) - ); - } - - public static changeMessage(msg: MessagesBase, messageToEls: MessageToElements[0], messageBody: MessageBody) { - if (messageToEls) { - if (MessageUtils.isElementActive(messageToEls[1])) { - return console.error('Cannot update a message that is being streamed'); - } - if (messageBody.text) { - MessageUtils.changeTextMessage(msg, messageToEls, messageBody.text); - } - if (messageBody.html) { - MessageUtils.changeHTMLMessage(msg, messageToEls, messageBody.html); - } - if (messageBody.files) { - // adds and removes - MessageUtils.changeFileMessages(msg, messageToEls, messageBody.files); - } else { - // removes elements - MessageUtils.removeFilesMessages(msg, messageToEls); - } - // Important to remove after elements are changed as existing element indexes are used - if (!messageBody.text && messageToEls[1].text) { - MessageUtils.removeTextHTMLMessage(msg, messageToEls, 'text'); - } - if (!messageBody.html && messageToEls[1].html) { - MessageUtils.removeTextHTMLMessage(msg, messageToEls, 'html'); - } - } else { - console.error('Message index not found. Please use the `getMessages` method to find the correct index'); - } - } } diff --git a/component/src/views/chat/messages/utils/updateMessage.ts b/component/src/views/chat/messages/utils/updateMessage.ts new file mode 100644 index 000000000..450cf3439 --- /dev/null +++ b/component/src/views/chat/messages/utils/updateMessage.ts @@ -0,0 +1,122 @@ +import {MessageToElements, MessageBodyElements, MessageBody} from '../../../../types/messagesInternal'; +import {MessageFile} from '../../../../types/messageFile'; +import {MessageStyles} from '../../../../types/messages'; +import {MessageElements, Messages} from '../messages'; +import {FileMessageUtils} from './fileMessageUtils'; +import {HTMLMessages} from '../html/htmlMessages'; +import {FileMessages} from '../fileMessages'; +import {MessagesBase} from '../messagesBase'; + +export class UpdateMessage { + private static removeElements(messageElementRefs: MessageElements[], elemsToRemove?: MessageElements) { + if (!elemsToRemove) return; + const removalElsIndex = messageElementRefs.findIndex((messageElements) => messageElements === elemsToRemove); + messageElementRefs.splice(removalElsIndex, 1); + elemsToRemove?.outerContainer.remove(); + } + + private static removeFilesMessages(msg: MessagesBase, messageToEls: MessageToElements[0]) { + messageToEls[1].files?.forEach((file) => { + UpdateMessage.removeElements(msg.messageElementRefs, file); + }); + delete messageToEls[0].files; + delete messageToEls[1].files; + } + + private static removeTextHTMLMessage(msg: MessagesBase, messageToEls: MessageToElements[0], type: 'text' | 'html') { + const elemsToRemove = messageToEls[1][type]; + UpdateMessage.removeElements(msg.messageElementRefs, elemsToRemove); + delete messageToEls[0][type]; + delete messageToEls[1][type]; + } + + private static updateHTMLMessage(msg: MessagesBase, messageToEls: MessageToElements[0], newHTML: string) { + if (messageToEls[1].html) { + HTMLMessages.overwriteElements(msg, newHTML, messageToEls[1].html); + } else { + const messageElements = HTMLMessages.create(msg, newHTML, messageToEls[0].role); + const previousElements = (messageToEls[1].files?.[messageToEls[1].files?.length - 1] || + messageToEls[1].text) as MessageElements; + msg.elementRef.insertBefore(messageElements.outerContainer, previousElements.outerContainer.nextSibling); + msg.messageElementRefs.splice(msg.messageElementRefs.length - 1, 1); // removing as createMessageElements adds one + const prevMsgElsIndex = msg.messageElementRefs.findIndex((messageElements) => messageElements === previousElements); + msg.messageElementRefs.splice(prevMsgElsIndex + 1, 0, messageElements); + messageToEls[1].html = messageElements; + } + messageToEls[0].html = newHTML; + } + + // finds beforeElement, creates new elements, remove old and adds new ones + private static updateFileMessages(msg: MessagesBase, messageToEls: MessageToElements[0], newFiles: MessageFile[]) { + const role = messageToEls[0].role; + const typeToElements = FileMessages.createMessages(msg, newFiles, role); + const nextElement = messageToEls[1].html; + const prevElement = messageToEls[1].files?.[messageToEls[1].files?.length - 1] || messageToEls[1].text; + const siblingElement = (nextElement || prevElement) as MessageElements; + let siblingElementIndex = msg.messageElementRefs.findIndex((messageElements) => messageElements === siblingElement); + if (prevElement) siblingElementIndex += 1; + const beforeElement = (nextElement?.outerContainer || prevElement?.outerContainer.nextSibling) as Node; + typeToElements.forEach(({type, elements}, index) => { + FileMessageUtils.setElementProps(msg, elements, type as keyof MessageStyles, role); + msg.elementRef.insertBefore(elements.outerContainer, beforeElement); + msg.messageElementRefs.splice(msg.messageElementRefs.length - 1, 1); // removing as createMessageElements adds one + msg.messageElementRefs.splice(siblingElementIndex + index, 0, elements); + }); + UpdateMessage.removeFilesMessages(msg, messageToEls); + messageToEls[1].files = typeToElements.map(({elements}) => elements); + messageToEls[0].files = newFiles; + } + + private static updateTextMessage(msg: MessagesBase, messageToEls: MessageToElements[0], newText: string) { + if (messageToEls[1].text) { + msg.renderText(messageToEls[1].text.bubbleElement, newText); + } else { + const messageElements = msg.createElements(newText, messageToEls[0].role); + const nextElements = (messageToEls[1].files?.[0] || messageToEls[1].html) as MessageElements; + msg.elementRef.insertBefore(messageElements.outerContainer, nextElements.outerContainer); + const nextMsgElsIndex = msg.messageElementRefs.findIndex((messageElements) => messageElements === nextElements); + msg.messageElementRefs.splice(nextMsgElsIndex, 0, messageElements); + messageToEls[1].text = messageElements; + } + messageToEls[0].text = newText; + } + + private static isElementActive(elements: MessageBodyElements) { + return ( + Messages.isActiveElement(elements.text?.bubbleElement.classList) || + Messages.isActiveElement(elements.html?.bubbleElement.classList) + ); + } + + // note that overwrite and 'deep-chat-temporary-message' are used to remove a message + public static update(msg: MessagesBase, index: number, messageBody: MessageBody) { + const messageToEls = msg.messageToElements[index]; + if (messageToEls) { + if (UpdateMessage.isElementActive(messageToEls[1])) { + return console.error('Cannot update a message that is being streamed'); + } + if (messageBody.text) { + UpdateMessage.updateTextMessage(msg, messageToEls, messageBody.text); + } + if (messageBody.html) { + UpdateMessage.updateHTMLMessage(msg, messageToEls, messageBody.html); + } + if (messageBody.files) { + // adds and removes + UpdateMessage.updateFileMessages(msg, messageToEls, messageBody.files); + } else { + // removes elements + UpdateMessage.removeFilesMessages(msg, messageToEls); + } + // Important to remove after elements are changed as existing element indexes are used + if (!messageBody.text && messageToEls[1].text) { + UpdateMessage.removeTextHTMLMessage(msg, messageToEls, 'text'); + } + if (!messageBody.html && messageToEls[1].html) { + UpdateMessage.removeTextHTMLMessage(msg, messageToEls, 'html'); + } + } else { + console.error('Message index not found. Please use the `getMessages` method to find the correct index'); + } + } +} diff --git a/component/src/webModel/webModel.ts b/component/src/webModel/webModel.ts index 3c3e15a5e..0743ac8cf 100644 --- a/component/src/webModel/webModel.ts +++ b/component/src/webModel/webModel.ts @@ -1,7 +1,7 @@ import {MessageStream} from '../views/chat/messages/stream/messageStream'; import {AppConfig, ChatOptions} from '../types/webModel/webLLM/webLLM'; +import {MessageUtils} from '../views/chat/messages/utils/messageUtils'; import {ErrorMessages} from '../utils/errorMessages/errorMessages'; -import {MessageUtils} from '../views/chat/messages/messageUtils'; import {IntroMessage, MessageContent} from '../types/messages'; import {BaseServiceIO} from '../services/utils/baseServiceIO'; import {WebModelIntroMessage} from './webModelIntroMessage';