diff --git a/public/script.js b/public/script.js index bd3a39f54..9b8ab0c3f 100644 --- a/public/script.js +++ b/public/script.js @@ -3987,37 +3987,54 @@ export function replaceBiasMarkup(str) { return (str ?? '').replace(/\{\{[\s\S]*?\}\}/gm, ''); } -export async function sendMessageAsUser(textareaText, messageBias) { - textareaText = getRegexedString(textareaText, regex_placement.USER_INPUT); +/** + * Inserts a user message into the chat history. + * @param {string} messageText Message text. + * @param {string} messageBias Message bias. + * @param {number} [insertAt] Optional index to insert the message at. + * @returns {Promise} A promise that resolves when the message is inserted. + */ +export async function sendMessageAsUser(messageText, messageBias, insertAt = null) { + messageText = getRegexedString(messageText, regex_placement.USER_INPUT); - chat[chat.length] = {}; - chat[chat.length - 1]['name'] = name1; - chat[chat.length - 1]['is_user'] = true; - chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); - chat[chat.length - 1]['mes'] = substituteParams(textareaText); - chat[chat.length - 1]['extra'] = {}; + const message = { + name: name1, + is_user: true, + is_system: false, + send_date: getMessageTimeStamp(), + mes: substituteParams(messageText), + extra: {}, + }; if (power_user.message_token_count_enabled) { - chat[chat.length - 1]['extra']['token_count'] = getTokenCount(chat[chat.length - 1]['mes'], 0); + message.extra.token_count = getTokenCount(message.mes, 0); } // Lock user avatar to a persona. if (user_avatar in power_user.personas) { - chat[chat.length - 1]['force_avatar'] = getUserAvatar(user_avatar); + message.force_avatar = getUserAvatar(user_avatar); } if (messageBias) { - console.debug('checking bias'); - chat[chat.length - 1]['extra']['bias'] = messageBias; + message.extra.bias = messageBias; + } + + await populateFileAttachment(message); + statMesProcess(message, 'user', characters, this_chid, ''); + + if (typeof insertAt === 'number' && insertAt >= 0 && insertAt <= chat.length) { + chat.splice(insertAt, 0, message); + await saveChatConditional(); + await eventSource.emit(event_types.MESSAGE_SENT, insertAt); + await reloadCurrentChat(); + await eventSource.emit(event_types.USER_MESSAGE_RENDERED, insertAt); + } else { + chat.push(message); + const chat_id = (chat.length - 1); + await eventSource.emit(event_types.MESSAGE_SENT, chat_id); + addOneMessage(message); + await eventSource.emit(event_types.USER_MESSAGE_RENDERED, chat_id); } - await populateFileAttachment(chat[chat.length - 1]); - statMesProcess(chat[chat.length - 1], 'user', characters, this_chid, ''); - // Wait for all handlers to finish before continuing with the prompt - const chat_id = (chat.length - 1); - await eventSource.emit(event_types.MESSAGE_SENT, chat_id); - addOneMessage(chat[chat_id]); - await eventSource.emit(event_types.USER_MESSAGE_RENDERED, chat_id); - console.debug('message sent as user'); } function getMaxContextSize() { diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index bc4c1f266..af9c47f84 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -628,7 +628,7 @@ async function askCharacter(_, text) { setCharacterName(character.name); - sendMessageAsUser(mesText) + await sendMessageAsUser(mesText, ''); const restoreCharacter = () => { setCharacterId(prevChId); @@ -888,7 +888,7 @@ async function triggerGenerationCallback(_, arg) { return ''; } -async function sendUserMessageCallback(_, text) { +async function sendUserMessageCallback(args, text) { if (!text) { console.warn('WARN: No text provided for /send command'); return; @@ -896,7 +896,8 @@ async function sendUserMessageCallback(_, text) { text = text.trim(); const bias = extractMessageBias(text); - await sendMessageAsUser(text, bias); + const insertAt = Number(resolveVariable(args?.at)); + await sendMessageAsUser(text, bias, insertAt); return ''; } @@ -1047,7 +1048,7 @@ async function setNarratorName(_, text) { await saveChatConditional(); } -export async function sendMessageAs(namedArgs, text) { +export async function sendMessageAs(args, text) { if (!text) { return; } @@ -1055,8 +1056,8 @@ export async function sendMessageAs(namedArgs, text) { let name; let mesText; - if (namedArgs.name) { - name = namedArgs.name.trim(); + if (args.name) { + name = args.name.trim(); mesText = text.trim(); if (!name && !text) { @@ -1107,14 +1108,24 @@ export async function sendMessageAs(namedArgs, text) { } }; - chat.push(message); - await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1)); - addOneMessage(message); - await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, (chat.length - 1)); - await saveChatConditional(); + const insertAt = Number(resolveVariable(args.at)); + + if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) { + chat.splice(insertAt, 0, message); + await saveChatConditional(); + await eventSource.emit(event_types.MESSAGE_RECEIVED, insertAt); + await reloadCurrentChat(); + await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, insertAt); + } else { + chat.push(message); + await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1)); + addOneMessage(message); + await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, (chat.length - 1)); + await saveChatConditional(); + } } -export async function sendNarratorMessage(_, text) { +export async function sendNarratorMessage(args, text) { if (!text) { return; } @@ -1138,11 +1149,21 @@ export async function sendNarratorMessage(_, text) { }, }; - chat.push(message); - await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1)); - addOneMessage(message); - await eventSource.emit(event_types.USER_MESSAGE_RENDERED, (chat.length - 1)); - await saveChatConditional(); + const insertAt = Number(resolveVariable(args.at)); + + if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) { + chat.splice(insertAt, 0, message); + await saveChatConditional(); + await eventSource.emit(event_types.MESSAGE_SENT, insertAt); + await reloadCurrentChat(); + await eventSource.emit(event_types.USER_MESSAGE_RENDERED, insertAt); + } else { + chat.push(message); + await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1)); + addOneMessage(message); + await eventSource.emit(event_types.USER_MESSAGE_RENDERED, (chat.length - 1)); + await saveChatConditional(); + } } export async function promptQuietForLoudResponse(who, text) { @@ -1184,7 +1205,7 @@ export async function promptQuietForLoudResponse(who, text) { } -async function sendCommentMessage(_, text) { +async function sendCommentMessage(args, text) { if (!text) { return; } @@ -1202,11 +1223,21 @@ async function sendCommentMessage(_, text) { }, }; - chat.push(message); - await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1)); - addOneMessage(message); - await eventSource.emit(event_types.USER_MESSAGE_RENDERED, (chat.length - 1)); - await saveChatConditional(); + const insertAt = Number(resolveVariable(args.at)); + + if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) { + chat.splice(insertAt, 0, message); + await saveChatConditional(); + await eventSource.emit(event_types.MESSAGE_SENT, insertAt); + await reloadCurrentChat(); + await eventSource.emit(event_types.USER_MESSAGE_RENDERED, insertAt); + } else { + chat.push(message); + await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1)); + addOneMessage(message); + await eventSource.emit(event_types.USER_MESSAGE_RENDERED, (chat.length - 1)); + await saveChatConditional(); + } } /**