diff --git a/public/scripts/macros.js b/public/scripts/macros.js index 504b05596..a9df01987 100644 --- a/public/scripts/macros.js +++ b/public/scripts/macros.js @@ -7,122 +7,103 @@ import { replaceVariableMacros } from './variables.js'; // Register any macro that you want to leave in the compiled story string Handlebars.registerHelper('trim', () => '{{trim}}'); -/** - * Returns the ID of the last message in the chat. - * @returns {string} The ID of the last message in the chat. - */ -function getLastMessageId() { - const index = chat?.length - 1; - if (!isNaN(index) && index >= 0) { - return String(index); +/** + * Returns the ID of the last message in the chat + * + * Optionally can only choose specific messages, if a filter is provided. + * + * @param {object} param0 - Optional arguments + * @param {boolean} [param0.exclude_swipe_in_propress=true] - Whether a message that is currently being swiped should be ignored + * @param {function(object):boolean} [param0.filter] - A filter applied to the search, ignoring all messages that don't match the criteria. For example to only find user messages, etc. + * @returns {number|null} The message id, or null if none was found + */ +export function getLastMessageId({ exclude_swipe_in_propress = true, filter = null } = {}) { + for (let i = chat?.length - 1; i >= 0; i--) { + let message = chat[i]; + + // If ignoring swipes and the message is being swiped, continue + // We can check if a message is being swiped by checking whether the current swipe id is not in the list of finished swipes yet + if (exclude_swipe_in_propress && message.swipes && message.swipe_id >= message.swipes.length) { + continue; + } + + // Check if no filter is provided, or if the message passes the filter + if (!filter || filter(message)) { + return i; + } } - return ''; + return null; } /** - * Returns the ID of the first message included in the context. - * @returns {string} The ID of the first message in the context. + * Returns the ID of the first message included in the context + * + * @returns {number|null} The ID of the first message in the context */ function getFirstIncludedMessageId() { - const index = document.querySelector('.lastInContext')?.getAttribute('mesid'); + const index = Number(document.querySelector('.lastInContext')?.getAttribute('mesid')); if (!isNaN(index) && index >= 0) { - return String(index); + return index; } - return ''; + return null; } /** - * Returns the last message in the chat. - * @returns {string} The last message in the chat. + * Returns the last message in the chat + * + * @returns {string} The last message in the chat */ function getLastMessage() { - const index = chat?.length - 1; - - if (!isNaN(index) && index >= 0) { - return chat[index].mes; - } - - return ''; + const mid = getLastMessageId(); + return chat[mid]?.mes ?? ''; } /** - * Returns the last message from the user. - * @returns {string} The last message from the user. + * Returns the last message from the user + * + * @returns {string} The last message from the user */ function getLastUserMessage() { - if (!Array.isArray(chat) || chat.length === 0) { - return ''; - } - - for (let i = chat.length - 1; i >= 0; i--) { - if (chat[i].is_user && !chat[i].is_system) { - return chat[i].mes; - } - } - - return ''; + const mid = getLastMessageId({ filter: m => m.is_user && !m.is_system }); + return chat[mid]?.mes ?? ''; } /** - * Returns the last message from the bot. - * @returns {string} The last message from the bot. + * Returns the last message from the bot + * + * @returns {string} The last message from the bot */ function getLastCharMessage() { - if (!Array.isArray(chat) || chat.length === 0) { - return ''; - } - - for (let i = chat.length - 1; i >= 0; i--) { - if (!chat[i].is_user && !chat[i].is_system) { - return chat[i].mes; - } - } - - return ''; + const mid = getLastMessageId({ filter: m => !m.is_user && !m.is_system }); + return chat[mid]?.mes ?? ''; } /** - * Returns the ID of the last swipe. - * @returns {string} The 1-based ID of the last swipe + * Returns the 1-based ID (number) of the last swipe + * + * @returns {number|null} The 1-based ID of the last swipe */ function getLastSwipeId() { - const index = chat?.length - 1; - - if (!isNaN(index) && index >= 0) { - const swipes = chat[index].swipes; - - if (!Array.isArray(swipes) || swipes.length === 0) { - return ''; - } - - return String(swipes.length); - } - - return ''; + // For swipe macro, we are accepting using the message that is currently being swiped + const mid = getLastMessageId({ exclude_swipe_in_propress: false }); + const swipes = chat[mid]?.swipes; + return swipes?.length; } /** - * Returns the ID of the current swipe. - * @returns {string} The 1-based ID of the current swipe. + * Returns the 1-based ID (number) of the current swipe + * + * @returns {number|null} The 1-based ID of the current swipe */ function getCurrentSwipeId() { - const index = chat?.length - 1; - - if (!isNaN(index) && index >= 0) { - const swipeId = chat[index].swipe_id; - - if (swipeId === undefined || isNaN(swipeId)) { - return ''; - } - - return String(swipeId + 1); - } - - return ''; + // For swipe macro, we are accepting using the message that is currently being swiped + const mid = getLastMessageId({ exclude_swipe_in_propress: false }); + const swipeId = chat[mid]?.swipe_id; + return swipeId ? swipeId + 1 : null; } /** @@ -292,12 +273,12 @@ export function evaluateMacros(content, env) { content = content.replace(/{{maxPrompt}}/gi, () => String(getMaxContextSize())); content = content.replace(/{{lastMessage}}/gi, () => getLastMessage()); - content = content.replace(/{{lastMessageId}}/gi, () => getLastMessageId()); + content = content.replace(/{{lastMessageId}}/gi, () => String(getLastMessageId() ?? '')); content = content.replace(/{{lastUserMessage}}/gi, () => getLastUserMessage()); content = content.replace(/{{lastCharMessage}}/gi, () => getLastCharMessage()); - content = content.replace(/{{firstIncludedMessageId}}/gi, () => getFirstIncludedMessageId()); - content = content.replace(/{{lastSwipeId}}/gi, () => getLastSwipeId()); - content = content.replace(/{{currentSwipeId}}/gi, () => getCurrentSwipeId()); + content = content.replace(/{{firstIncludedMessageId}}/gi, () => String(getFirstIncludedMessageId() ?? '')); + content = content.replace(/{{lastSwipeId}}/gi, () => String(getLastSwipeId() ?? '')); + content = content.replace(/{{currentSwipeId}}/gi, () => String(getCurrentSwipeId() ?? '')); content = content.replace(/\{\{\/\/([\s\S]*?)\}\}/gm, '');