diff --git a/public/scripts/extensions/hypebot/index.js b/public/scripts/extensions/hypebot/index.js index 69b08ac5a..11430226b 100644 --- a/public/scripts/extensions/hypebot/index.js +++ b/public/scripts/extensions/hypebot/index.js @@ -6,6 +6,7 @@ import { bufferToBase64, debounce } from "../../utils.js"; import { decodeTextTokens, getTextTokens, tokenizers } from "../../tokenizers.js"; const MODULE_NAME = 'hypebot'; +const WAITING_VERBS = ['thinking', 'typing', 'brainstorming', 'cooking', 'conjuring']; const MAX_PROMPT = 1024; const MAX_LENGTH = 50; const MAX_STRING_LENGTH = MAX_PROMPT * 4; @@ -20,8 +21,7 @@ const settings = { * @returns {string} Random waiting verb */ function getWaitingVerb() { - const waitingVerbs = ['thinking', 'typing', 'brainstorming', 'cooking', 'conjuring']; - return waitingVerbs[Math.floor(Math.random() * waitingVerbs.length)]; + return WAITING_VERBS[Math.floor(Math.random() * WAITING_VERBS.length)]; } /** @@ -49,8 +49,7 @@ function getVerb(text) { * @returns {string} Formatted HTML text */ function formatReply(text) { - const verb = getVerb(text); - return DOMPurify.sanitize(`${settings.name} ${verb}: ${text}`); + return `${settings.name} ${getVerb(text)}: ${text}`; } let hypeBotBar; @@ -58,13 +57,25 @@ let abortController; const generateDebounced = debounce(() => generateHypeBot(), 500); +/** + * Sets the HypeBot text. Preserves scroll position of the chat. + * @param {string} text Text to set + */ +function setHypeBotText(text) { + const blockA = $('#chat'); + var originalScrollBottom = blockA[0].scrollHeight - (blockA.scrollTop() + blockA.outerHeight()); + hypeBotBar.html(DOMPurify.sanitize(text)); + var newScrollTop = blockA[0].scrollHeight - (blockA.outerHeight() + originalScrollBottom); + blockA.scrollTop(newScrollTop); +} + /** * Called when a chat event occurs to generate a HypeBot reply. * @param {boolean} clear Clear the hypebot bar. */ function onChatEvent(clear) { if (clear) { - hypeBotBar.text(''); + setHypeBotText(''); } abortController?.abort(); @@ -80,12 +91,12 @@ async function generateHypeBot() { } if (!secret_state[SECRET_KEYS.NOVEL]) { - hypeBotBar.html('No API key found. Please enter your API key in the NovelAI API Settings'); + setHypeBotText('No API key found. Please enter your API key in the NovelAI API Settings'); return; } console.debug('Generating HypeBot reply'); - hypeBotBar.html(DOMPurify.sanitize(`${settings.name} is ${getWaitingVerb()}...`)); + setHypeBotText(`${settings.name} is ${getWaitingVerb()}...`); const context = getContext(); const chat = context.chat.slice(); @@ -160,7 +171,7 @@ async function generateHypeBot() { const ids = Array.from(new Uint16Array(Uint8Array.from(atob(data.output), c => c.charCodeAt(0)).buffer)); const output = decodeTextTokens(tokenizers.GPT2, ids).replace(/�/g, '').trim(); - hypeBotBar.html(formatReply(output)); + setHypeBotText(formatReply(output)); } }