let main_parent = document.getElementById('chat-tab').parentNode; let extensions = document.getElementById('extensions'); main_parent.childNodes[0].classList.add("header_bar"); main_parent.style = "padding: 0; margin: 0"; main_parent.parentNode.style = "gap: 0"; main_parent.parentNode.parentNode.style = "padding: 0"; document.querySelector('.header_bar').addEventListener('click', function(event) { if (event.target.tagName === 'BUTTON') { const buttonText = event.target.textContent.trim(); let chat_visible = (buttonText == 'Chat'); let default_visible = (buttonText == 'Default'); let notebook_visible = (buttonText == 'Notebook'); // Check if one of the generation tabs is visible if (chat_visible || notebook_visible || default_visible) { extensions.style.display = 'flex'; if (chat_visible) { extensions.style.maxWidth = "880px"; extensions.style.padding = "0px"; } else { extensions.style.maxWidth = "none"; extensions.style.padding = "15px"; } } else { extensions.style.display = 'none'; } } }); //------------------------------------------------ // Keyboard shortcuts //------------------------------------------------ document.addEventListener("keydown", function(event) { // Stop generation on Esc pressed if (event.key === "Escape") { // Find the element with id 'stop' and click it var stopButton = document.getElementById("stop"); if (stopButton) { stopButton.click(); } } // Show chat controls on Ctrl + S else if (event.ctrlKey && event.key == "s") { event.preventDefault(); var showControlsElement = document.getElementById('show-controls'); if (showControlsElement && showControlsElement.childNodes.length >= 4) { showControlsElement.childNodes[3].click(); var arr = document.getElementById('chat-input').childNodes[2].childNodes; arr[arr.length - 1].focus(); } } // Regenerate on Ctrl + Enter else if (event.ctrlKey && event.key === 'Enter') { event.preventDefault(); document.getElementById('Regenerate').click(); } // Continue on Alt + Enter else if (event.altKey && event.key === 'Enter') { event.preventDefault(); document.getElementById('Continue').click(); } // Remove last on Ctrl + Shift + Backspace else if (event.ctrlKey && event.shiftKey && event.key === 'Backspace') { event.preventDefault(); document.getElementById('Remove-last').click(); } // Copy last on Ctrl + Shift + K else if (event.ctrlKey && event.shiftKey && event.key === 'K') { event.preventDefault(); document.getElementById('Copy-last').click(); } // Replace last on Ctrl + Shift + L else if (event.ctrlKey && event.shiftKey && event.key === 'L') { event.preventDefault(); document.getElementById('Replace-last').click(); } // Impersonate on Ctrl + Shift + M else if (event.ctrlKey && event.shiftKey && event.key === 'M') { event.preventDefault(); document.getElementById('Impersonate').click(); } }); //------------------------------------------------ // Position the chat typing dots //------------------------------------------------ typing = document.getElementById('typing-container'); typingParent = typing.parentNode; typingSibling = typing.previousElementSibling; typingSibling.insertBefore(typing, typingSibling.childNodes[2]); //------------------------------------------------ // Chat scrolling //------------------------------------------------ const targetElement = document.getElementById('chat').parentNode.parentNode.parentNode; targetElement.classList.add('pretty_scrollbar'); targetElement.classList.add('chat-parent'); let isScrolled = false; targetElement.addEventListener('scroll', function() { let diff = targetElement.scrollHeight - targetElement.clientHeight; if(Math.abs(targetElement.scrollTop - diff) <= 1 || diff == 0) { isScrolled = false; } else { isScrolled = true; } }); // Create a MutationObserver instance const observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if(!isScrolled) { targetElement.scrollTop = targetElement.scrollHeight; } const firstChild = targetElement.children[0]; if (firstChild.classList.contains('generating')) { typing.parentNode.classList.add('visible-dots'); document.getElementById('stop').style.display = 'flex'; document.getElementById('Generate').style.display = 'none'; } else { typing.parentNode.classList.remove('visible-dots'); document.getElementById('stop').style.display = 'none'; document.getElementById('Generate').style.display = 'flex'; } }); }); // Configure the observer to watch for changes in the subtree and attributes const config = { childList: true, subtree: true, characterData: true, attributeOldValue: true, characterDataOldValue: true }; // Start observing the target element observer.observe(targetElement, config); //------------------------------------------------ // Notebook box scrolling //------------------------------------------------ const notebookElement = document.querySelector('#textbox-notebook textarea'); let notebookScrolled = false; notebookElement.addEventListener('scroll', function() { let diff = notebookElement.scrollHeight - notebookElement.clientHeight; if(Math.abs(notebookElement.scrollTop - diff) <= 1 || diff == 0) { notebookScrolled = false; } else { notebookScrolled = true; } }); const notebookObserver = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if(!notebookScrolled) { notebookElement.scrollTop = notebookElement.scrollHeight; } }); }); notebookObserver.observe(notebookElement.parentNode.parentNode.parentNode, config); //------------------------------------------------ // Default box scrolling //------------------------------------------------ const defaultElement = document.querySelector('#textbox-default textarea'); let defaultScrolled = false; defaultElement.addEventListener('scroll', function() { let diff = defaultElement.scrollHeight - defaultElement.clientHeight; if(Math.abs(defaultElement.scrollTop - diff) <= 1 || diff == 0) { defaultScrolled = false; } else { defaultScrolled = true; } }); const defaultObserver = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if(!defaultScrolled) { defaultElement.scrollTop = defaultElement.scrollHeight; } }); }); defaultObserver.observe(defaultElement.parentNode.parentNode.parentNode, config); //------------------------------------------------ // Add some scrollbars //------------------------------------------------ const textareaElements = document.querySelectorAll('.add_scrollbar textarea'); for(i = 0; i < textareaElements.length; i++) { textareaElements[i].classList.remove('scroll-hide'); textareaElements[i].classList.add('pretty_scrollbar'); textareaElements[i].style.resize = "none"; } //------------------------------------------------ // Improve the looks of the chat input field //------------------------------------------------ let isOld = document.querySelectorAll('.old-ui').length > 0; document.getElementById('chat-input').parentNode.style.background = 'transparent'; document.getElementById('chat-input').parentNode.style.border = 'none'; document.getElementById('chat-input').parentElement.parentElement.style.minWidth = 0; document.getElementById('stop').parentElement.parentElement.style.minWidth = 0; document.getElementById('stop').parentElement.parentElement.style.display = 'flex'; document.getElementById('stop').parentElement.parentElement.style.flexDirection = 'column-reverse'; document.getElementById('stop').parentElement.parentElement.style.paddingBottom = '3px'; document.getElementById('stop').parentElement.parentElement.parentElement.style.paddingBottom = isOld ? '0px' : '20px'; document.getElementById('stop').parentElement.parentElement.style.flex = '0 0 auto'; document.getElementById('gr-hover').parentElement.style.minWidth = 0; document.getElementById('gr-hover').parentElement.style.display = 'flex'; document.getElementById('gr-hover').parentElement.style.flexDirection = 'column-reverse'; document.getElementById('gr-hover').parentElement.style.flex = '0'; document.getElementById('gr-hover').parentElement.style.paddingRight = '20px'; document.getElementById('gr-hover').parentElement.style.paddingBottom = '3px'; //------------------------------------------------ // Remove some backgrounds //------------------------------------------------ const noBackgroundelements = document.querySelectorAll('.no-background'); for(i = 0; i < noBackgroundelements.length; i++) { noBackgroundelements[i].parentNode.style.border = 'none'; noBackgroundelements[i].parentNode.parentNode.parentNode.style.alignItems = 'center'; } //------------------------------------------------ // Create the hover menu in the chat tab // The show/hide events were adapted from: // https://github.com/SillyTavern/SillyTavern/blob/6c8bd06308c69d51e2eb174541792a870a83d2d6/public/script.js //------------------------------------------------ var buttonsInChat = document.querySelectorAll("#chat-tab #chat-buttons:not(.old-ui) button"); var button = document.getElementById('hover-element-button'); var menu = document.getElementById('hover-menu'); function showMenu() { menu.style.display = 'flex'; // Show the menu } function hideMenu() { menu.style.display = 'none'; // Hide the menu } if (buttonsInChat.length > 0) { for (let i = buttonsInChat.length - 1; i >= 0; i--) { const thisButton = buttonsInChat[i]; menu.appendChild(thisButton); if(i != 8) { thisButton.addEventListener("click", () => { hideMenu(); }); } const buttonText = thisButton.textContent; const matches = buttonText.match(/(\(.*?\))/); if (matches && matches.length > 1) { // Apply the transparent-substring class to the matched substring const substring = matches[1]; const newText = buttonText.replace(substring, ` ${substring}`); thisButton.innerHTML = newText; } } } else { buttonsInChat = document.querySelectorAll("#chat-tab #chat-buttons.old-ui button"); console.log(buttonsInChat); for (let i = 0; i < buttonsInChat.length; i++) { buttonsInChat[i].textContent = buttonsInChat[i].textContent.replace(/ \(.*?\)/, ''); } document.getElementById('gr-hover').parentElement.style.display = 'none'; } function isMouseOverButtonOrMenu() { return menu.matches(':hover') || button.matches(':hover'); } button.addEventListener('mouseenter', function () { showMenu(); }); button.addEventListener('click', function () { showMenu(); }); // Add event listener for mouseleave on the button button.addEventListener('mouseleave', function () { // Delay to prevent menu hiding when the mouse leaves the button into the menu setTimeout(function () { if (!isMouseOverButtonOrMenu()) { hideMenu(); } }, 100); }); // Add event listener for mouseleave on the menu menu.addEventListener('mouseleave', function () { // Delay to prevent menu hide when the mouse leaves the menu into the button setTimeout(function () { if (!isMouseOverButtonOrMenu()) { hideMenu(); } }, 100); }); // Add event listener for click anywhere in the document document.addEventListener('click', function (event) { // Check if the click is outside the button/menu and the menu is visible if (!isMouseOverButtonOrMenu() && menu.style.display === 'flex') { hideMenu(); } }); //------------------------------------------------ // Relocate the "Show controls" checkbox //------------------------------------------------ var elementToMove = document.getElementById('show-controls'); var parent = elementToMove.parentNode; for (var i = 0; i < 2; i++) { parent = parent.parentNode; } parent.insertBefore(elementToMove, parent.firstChild); //------------------------------------------------ // Make the chat input grow upwards instead of downwards //------------------------------------------------ document.getElementById('show-controls').parentNode.style.position = 'absolute'; document.getElementById('show-controls').parentNode.style.bottom = '0px'; //------------------------------------------------ // Focus on the chat input //------------------------------------------------ document.querySelector('#chat-input textarea').focus()