2023-10-08 04:07:57 +02:00
|
|
|
let main_parent = document.getElementById("chat-tab").parentNode;
|
|
|
|
let extensions = document.getElementById("extensions");
|
2023-03-15 20:34:31 +01:00
|
|
|
|
2023-08-13 06:12:15 +02:00
|
|
|
main_parent.childNodes[0].classList.add("header_bar");
|
|
|
|
main_parent.style = "padding: 0; margin: 0";
|
2023-08-30 01:15:12 +02:00
|
|
|
main_parent.parentNode.style = "gap: 0";
|
2023-08-13 06:12:15 +02:00
|
|
|
main_parent.parentNode.parentNode.style = "padding: 0";
|
|
|
|
|
2023-10-08 04:07:57 +02:00
|
|
|
document.querySelector(".header_bar").addEventListener("click", function(event) {
|
2024-07-03 05:57:03 +02:00
|
|
|
if (event.target.tagName !== "BUTTON") return;
|
2023-10-08 04:07:57 +02:00
|
|
|
|
2024-07-03 05:57:03 +02:00
|
|
|
const buttonText = event.target.textContent.trim();
|
|
|
|
const extensionsVisible = ["Chat", "Default", "Notebook"].includes(buttonText);
|
|
|
|
const chatVisible = buttonText === "Chat";
|
|
|
|
const showControlsChecked = document.querySelector("#show-controls input").checked;
|
|
|
|
const extensions = document.querySelector("#extensions");
|
2023-10-08 04:07:57 +02:00
|
|
|
|
2024-07-03 05:57:03 +02:00
|
|
|
if (extensionsVisible) {
|
|
|
|
if (extensions) {
|
|
|
|
extensions.style.display = "flex";
|
|
|
|
}
|
2024-12-17 04:47:41 +01:00
|
|
|
|
2024-07-03 05:57:03 +02:00
|
|
|
this.style.marginBottom = chatVisible ? "0px" : "19px";
|
2023-12-25 23:43:12 +01:00
|
|
|
|
2024-07-03 05:57:03 +02:00
|
|
|
if (chatVisible && !showControlsChecked) {
|
2024-12-17 04:47:41 +01:00
|
|
|
document.querySelectorAll(
|
|
|
|
"#chat-tab > div > :nth-child(1), #chat-tab > div > :nth-child(3), #chat-tab > div > :nth-child(4), #extensions"
|
|
|
|
).forEach(element => {
|
2024-07-03 05:57:03 +02:00
|
|
|
element.style.display = "none";
|
|
|
|
});
|
2023-03-15 20:34:31 +01:00
|
|
|
}
|
2024-12-17 04:47:41 +01:00
|
|
|
|
2024-07-03 05:57:03 +02:00
|
|
|
} else {
|
|
|
|
this.style.marginBottom = "19px";
|
|
|
|
if (extensions) extensions.style.display = "none";
|
2023-10-08 04:07:57 +02:00
|
|
|
}
|
2023-03-15 20:34:31 +01:00
|
|
|
});
|
2023-08-02 17:02:36 +02:00
|
|
|
|
2023-08-17 20:06:18 +02:00
|
|
|
//------------------------------------------------
|
2023-08-27 22:06:01 +02:00
|
|
|
// Keyboard shortcuts
|
2023-08-17 20:06:18 +02:00
|
|
|
//------------------------------------------------
|
2024-01-10 01:27:50 +01:00
|
|
|
let previousTabId = "chat-tab-button";
|
2023-08-03 17:13:17 +02:00
|
|
|
document.addEventListener("keydown", function(event) {
|
2023-08-27 22:06:01 +02:00
|
|
|
|
|
|
|
// Stop generation on Esc pressed
|
2023-08-03 17:13:17 +02:00
|
|
|
if (event.key === "Escape") {
|
|
|
|
// Find the element with id 'stop' and click it
|
|
|
|
var stopButton = document.getElementById("stop");
|
|
|
|
if (stopButton) {
|
|
|
|
stopButton.click();
|
|
|
|
}
|
|
|
|
}
|
2023-08-27 22:06:01 +02:00
|
|
|
|
2023-09-14 04:21:39 +02:00
|
|
|
// Show chat controls on Ctrl + S
|
2023-08-27 22:06:01 +02:00
|
|
|
else if (event.ctrlKey && event.key == "s") {
|
|
|
|
event.preventDefault();
|
|
|
|
|
2023-10-08 04:07:57 +02:00
|
|
|
var showControlsElement = document.getElementById("show-controls");
|
2023-08-27 22:06:01 +02:00
|
|
|
if (showControlsElement && showControlsElement.childNodes.length >= 4) {
|
|
|
|
showControlsElement.childNodes[3].click();
|
2023-08-28 01:45:37 +02:00
|
|
|
|
2023-10-08 04:07:57 +02:00
|
|
|
var arr = document.getElementById("chat-input").childNodes[2].childNodes;
|
2023-08-28 01:45:37 +02:00
|
|
|
arr[arr.length - 1].focus();
|
2023-08-27 22:06:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-14 04:21:39 +02:00
|
|
|
// Regenerate on Ctrl + Enter
|
2023-10-08 04:07:57 +02:00
|
|
|
else if (event.ctrlKey && event.key === "Enter") {
|
2023-09-14 04:21:39 +02:00
|
|
|
event.preventDefault();
|
2023-10-08 04:07:57 +02:00
|
|
|
document.getElementById("Regenerate").click();
|
2023-09-14 04:21:39 +02:00
|
|
|
}
|
|
|
|
|
2023-09-14 12:59:12 +02:00
|
|
|
// Continue on Alt + Enter
|
2023-10-08 04:07:57 +02:00
|
|
|
else if (event.altKey && event.key === "Enter") {
|
2023-09-14 04:21:39 +02:00
|
|
|
event.preventDefault();
|
2023-10-08 04:07:57 +02:00
|
|
|
document.getElementById("Continue").click();
|
2023-09-14 04:21:39 +02:00
|
|
|
}
|
|
|
|
|
2023-09-14 11:33:06 +02:00
|
|
|
// Remove last on Ctrl + Shift + Backspace
|
2023-10-08 04:07:57 +02:00
|
|
|
else if (event.ctrlKey && event.shiftKey && event.key === "Backspace") {
|
2023-09-14 04:21:39 +02:00
|
|
|
event.preventDefault();
|
2023-10-08 04:07:57 +02:00
|
|
|
document.getElementById("Remove-last").click();
|
2023-09-14 04:21:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Copy last on Ctrl + Shift + K
|
2023-10-08 04:07:57 +02:00
|
|
|
else if (event.ctrlKey && event.shiftKey && event.key === "K") {
|
2023-09-14 04:21:39 +02:00
|
|
|
event.preventDefault();
|
2023-10-08 04:07:57 +02:00
|
|
|
document.getElementById("Copy-last").click();
|
2023-09-14 04:21:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Replace last on Ctrl + Shift + L
|
2023-10-08 04:07:57 +02:00
|
|
|
else if (event.ctrlKey && event.shiftKey && event.key === "L") {
|
2023-09-14 04:21:39 +02:00
|
|
|
event.preventDefault();
|
2023-10-08 04:07:57 +02:00
|
|
|
document.getElementById("Replace-last").click();
|
2023-09-14 04:21:39 +02:00
|
|
|
}
|
|
|
|
|
2023-09-14 11:33:06 +02:00
|
|
|
// Impersonate on Ctrl + Shift + M
|
2023-10-08 04:07:57 +02:00
|
|
|
else if (event.ctrlKey && event.shiftKey && event.key === "M") {
|
2023-09-14 04:21:39 +02:00
|
|
|
event.preventDefault();
|
2023-10-08 04:07:57 +02:00
|
|
|
document.getElementById("Impersonate").click();
|
2023-09-14 04:21:39 +02:00
|
|
|
}
|
|
|
|
|
2023-08-03 17:13:17 +02:00
|
|
|
});
|
2023-08-17 06:03:40 +02:00
|
|
|
|
2023-08-28 22:50:36 +02:00
|
|
|
//------------------------------------------------
|
|
|
|
// Position the chat typing dots
|
|
|
|
//------------------------------------------------
|
2023-10-08 04:07:57 +02:00
|
|
|
typing = document.getElementById("typing-container");
|
2023-08-28 22:50:36 +02:00
|
|
|
typingParent = typing.parentNode;
|
|
|
|
typingSibling = typing.previousElementSibling;
|
|
|
|
typingSibling.insertBefore(typing, typingSibling.childNodes[2]);
|
|
|
|
|
2023-08-17 20:06:18 +02:00
|
|
|
//------------------------------------------------
|
2023-08-17 06:03:40 +02:00
|
|
|
// Chat scrolling
|
2023-08-17 20:06:18 +02:00
|
|
|
//------------------------------------------------
|
2023-10-08 04:07:57 +02:00
|
|
|
const targetElement = document.getElementById("chat").parentNode.parentNode.parentNode;
|
|
|
|
targetElement.classList.add("pretty_scrollbar");
|
|
|
|
targetElement.classList.add("chat-parent");
|
2023-08-28 21:03:20 +02:00
|
|
|
let isScrolled = false;
|
|
|
|
|
2023-10-08 04:07:57 +02:00
|
|
|
targetElement.addEventListener("scroll", function() {
|
2023-08-28 21:03:20 +02:00
|
|
|
let diff = targetElement.scrollHeight - targetElement.clientHeight;
|
2023-09-19 22:12:34 +02:00
|
|
|
if(Math.abs(targetElement.scrollTop - diff) <= 10 || diff == 0) {
|
2023-08-28 21:03:20 +02:00
|
|
|
isScrolled = false;
|
|
|
|
} else {
|
|
|
|
isScrolled = true;
|
|
|
|
}
|
2024-04-26 15:13:11 +02:00
|
|
|
|
|
|
|
doSyntaxHighlighting();
|
|
|
|
|
2023-08-28 21:03:20 +02:00
|
|
|
});
|
2023-08-17 06:03:40 +02:00
|
|
|
|
|
|
|
// Create a MutationObserver instance
|
|
|
|
const observer = new MutationObserver(function(mutations) {
|
2024-05-07 01:51:05 +02:00
|
|
|
updateCssProperties();
|
|
|
|
|
2024-12-17 04:47:41 +01:00
|
|
|
if (targetElement.classList.contains("_generating")) {
|
2024-05-07 01:51:05 +02:00
|
|
|
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";
|
|
|
|
}
|
2023-08-28 22:50:36 +02:00
|
|
|
|
2024-05-07 01:51:05 +02:00
|
|
|
|
|
|
|
doSyntaxHighlighting();
|
2024-04-26 15:13:11 +02:00
|
|
|
|
|
|
|
if(!isScrolled) {
|
|
|
|
targetElement.scrollTop = targetElement.scrollHeight;
|
|
|
|
}
|
|
|
|
|
2023-08-17 06:03:40 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
// 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);
|
2023-08-17 20:06:18 +02:00
|
|
|
|
2024-04-26 15:13:11 +02:00
|
|
|
//------------------------------------------------
|
|
|
|
// Handle syntax highlighting / LaTeX
|
|
|
|
//------------------------------------------------
|
|
|
|
function isElementVisibleOnScreen(element) {
|
|
|
|
const rect = element.getBoundingClientRect();
|
|
|
|
return (
|
|
|
|
rect.left < window.innerWidth &&
|
|
|
|
rect.right > 0 &&
|
|
|
|
rect.top < window.innerHeight &&
|
|
|
|
rect.bottom > 0
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getVisibleMessagesIndexes() {
|
|
|
|
const elements = document.querySelectorAll(".message-body");
|
|
|
|
const visibleIndexes = [];
|
|
|
|
|
|
|
|
elements.forEach((element, index) => {
|
|
|
|
if (isElementVisibleOnScreen(element) && !element.hasAttribute("data-highlighted")) {
|
|
|
|
visibleIndexes.push(index);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return visibleIndexes;
|
|
|
|
}
|
|
|
|
|
|
|
|
function doSyntaxHighlighting() {
|
|
|
|
const indexes = getVisibleMessagesIndexes();
|
|
|
|
const elements = document.querySelectorAll(".message-body");
|
|
|
|
|
|
|
|
if (indexes.length > 0) {
|
|
|
|
observer.disconnect();
|
|
|
|
|
|
|
|
indexes.forEach((index) => {
|
|
|
|
const element = elements[index];
|
|
|
|
|
2024-05-07 01:51:05 +02:00
|
|
|
// Tag this element to prevent it from being highlighted twice
|
|
|
|
element.setAttribute("data-highlighted", "true");
|
|
|
|
|
2024-04-26 15:13:11 +02:00
|
|
|
// Perform syntax highlighting
|
|
|
|
const codeBlocks = element.querySelectorAll("pre code");
|
|
|
|
|
|
|
|
codeBlocks.forEach((codeBlock) => {
|
|
|
|
hljs.highlightElement(codeBlock);
|
|
|
|
});
|
|
|
|
|
|
|
|
renderMathInElement(element, {
|
|
|
|
delimiters: [
|
|
|
|
{ left: "$$", right: "$$", display: true },
|
2024-07-27 08:03:53 +02:00
|
|
|
{ left: "$", right: "$", display: false },
|
2024-04-26 15:13:11 +02:00
|
|
|
{ left: "\\(", right: "\\)", display: false },
|
|
|
|
{ left: "\\[", right: "\\]", display: true },
|
|
|
|
],
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
observer.observe(targetElement, config);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-28 21:03:20 +02:00
|
|
|
//------------------------------------------------
|
|
|
|
// Add some scrollbars
|
|
|
|
//------------------------------------------------
|
2023-10-08 04:07:57 +02:00
|
|
|
const textareaElements = document.querySelectorAll(".add_scrollbar textarea");
|
2023-08-28 21:03:20 +02:00
|
|
|
for(i = 0; i < textareaElements.length; i++) {
|
2023-10-08 04:07:57 +02:00
|
|
|
textareaElements[i].classList.remove("scroll-hide");
|
|
|
|
textareaElements[i].classList.add("pretty_scrollbar");
|
|
|
|
textareaElements[i].style.resize = "none";
|
2023-08-28 21:03:20 +02:00
|
|
|
}
|
|
|
|
|
2023-08-23 05:18:16 +02:00
|
|
|
//------------------------------------------------
|
|
|
|
// Remove some backgrounds
|
|
|
|
//------------------------------------------------
|
2023-10-08 04:07:57 +02:00
|
|
|
const noBackgroundelements = document.querySelectorAll(".no-background");
|
2023-08-23 05:18:16 +02:00
|
|
|
for(i = 0; i < noBackgroundelements.length; i++) {
|
2023-10-08 04:07:57 +02:00
|
|
|
noBackgroundelements[i].parentNode.style.border = "none";
|
|
|
|
noBackgroundelements[i].parentNode.parentNode.parentNode.style.alignItems = "center";
|
2023-08-23 05:18:16 +02:00
|
|
|
}
|
2023-09-13 07:36:12 +02:00
|
|
|
|
2023-12-27 18:59:23 +01:00
|
|
|
const slimDropdownElements = document.querySelectorAll(".slim-dropdown");
|
2023-10-11 03:20:49 +02:00
|
|
|
for (i = 0; i < slimDropdownElements.length; i++) {
|
2023-12-27 18:59:23 +01:00
|
|
|
const parentNode = slimDropdownElements[i].parentNode;
|
|
|
|
parentNode.style.background = "transparent";
|
|
|
|
parentNode.style.border = "0";
|
2023-10-11 03:20:49 +02:00
|
|
|
}
|
|
|
|
|
2023-09-13 07:36:12 +02:00
|
|
|
//------------------------------------------------
|
|
|
|
// Create the hover menu in the chat tab
|
2023-09-13 20:10:46 +02:00
|
|
|
// The show/hide events were adapted from:
|
|
|
|
// https://github.com/SillyTavern/SillyTavern/blob/6c8bd06308c69d51e2eb174541792a870a83d2d6/public/script.js
|
2023-09-13 07:36:12 +02:00
|
|
|
//------------------------------------------------
|
2024-12-17 04:47:41 +01:00
|
|
|
var buttonsInChat = document.querySelectorAll("#chat-tab #chat-buttons button");
|
2023-10-08 04:07:57 +02:00
|
|
|
var button = document.getElementById("hover-element-button");
|
|
|
|
var menu = document.getElementById("hover-menu");
|
2024-01-04 17:52:11 +01:00
|
|
|
var istouchscreen = (navigator.maxTouchPoints > 0) || "ontouchstart" in document.documentElement;
|
2023-09-13 20:10:46 +02:00
|
|
|
|
|
|
|
function showMenu() {
|
2023-10-08 04:07:57 +02:00
|
|
|
menu.style.display = "flex"; // Show the menu
|
2023-09-13 20:10:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function hideMenu() {
|
2023-10-08 04:07:57 +02:00
|
|
|
menu.style.display = "none"; // Hide the menu
|
2024-01-04 17:52:11 +01:00
|
|
|
if (!istouchscreen) {
|
|
|
|
document.querySelector("#chat-input textarea").focus(); // Focus on the chat input
|
|
|
|
}
|
2023-09-13 20:10:46 +02:00
|
|
|
}
|
2023-09-13 07:36:12 +02:00
|
|
|
|
2023-09-16 05:39:37 +02:00
|
|
|
if (buttonsInChat.length > 0) {
|
2023-10-08 04:07:57 +02:00
|
|
|
for (let i = buttonsInChat.length - 1; i >= 0; i--) {
|
|
|
|
const thisButton = buttonsInChat[i];
|
|
|
|
menu.appendChild(thisButton);
|
|
|
|
|
|
|
|
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, ` <span class="transparent-substring">${substring.slice(1, -1)}</span>`);
|
|
|
|
thisButton.innerHTML = newText;
|
2023-09-16 05:39:37 +02:00
|
|
|
}
|
2023-10-08 04:07:57 +02:00
|
|
|
}
|
2023-09-13 07:36:12 +02:00
|
|
|
}
|
|
|
|
|
2023-09-13 20:10:46 +02:00
|
|
|
function isMouseOverButtonOrMenu() {
|
2023-10-08 04:07:57 +02:00
|
|
|
return menu.matches(":hover") || button.matches(":hover");
|
2023-09-13 20:10:46 +02:00
|
|
|
}
|
|
|
|
|
2023-10-08 04:07:57 +02:00
|
|
|
button.addEventListener("mouseenter", function () {
|
2024-01-04 17:52:11 +01:00
|
|
|
if (!istouchscreen) {
|
|
|
|
showMenu();
|
|
|
|
}
|
2023-09-13 20:10:46 +02:00
|
|
|
});
|
|
|
|
|
2023-10-08 04:07:57 +02:00
|
|
|
button.addEventListener("click", function () {
|
2024-01-04 17:52:11 +01:00
|
|
|
if (menu.style.display === "flex") {
|
|
|
|
hideMenu();
|
|
|
|
}
|
|
|
|
else {
|
2024-01-10 01:27:50 +01:00
|
|
|
showMenu();
|
2024-01-04 17:52:11 +01:00
|
|
|
}
|
2023-09-13 20:10:46 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
// Add event listener for mouseleave on the button
|
2023-10-08 04:07:57 +02:00
|
|
|
button.addEventListener("mouseleave", function () {
|
|
|
|
// Delay to prevent menu hiding when the mouse leaves the button into the menu
|
|
|
|
setTimeout(function () {
|
|
|
|
if (!isMouseOverButtonOrMenu()) {
|
|
|
|
hideMenu();
|
|
|
|
}
|
|
|
|
}, 100);
|
2023-09-13 20:10:46 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
// Add event listener for mouseleave on the menu
|
2023-10-08 04:07:57 +02:00
|
|
|
menu.addEventListener("mouseleave", function () {
|
|
|
|
// Delay to prevent menu hide when the mouse leaves the menu into the button
|
|
|
|
setTimeout(function () {
|
|
|
|
if (!isMouseOverButtonOrMenu()) {
|
|
|
|
hideMenu();
|
|
|
|
}
|
|
|
|
}, 100);
|
2023-09-13 20:10:46 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
// Add event listener for click anywhere in the document
|
2023-10-08 04:07:57 +02:00
|
|
|
document.addEventListener("click", function (event) {
|
2024-12-17 04:47:41 +01:00
|
|
|
const target = event.target;
|
|
|
|
|
2023-10-08 04:07:57 +02:00
|
|
|
// Check if the click is outside the button/menu and the menu is visible
|
|
|
|
if (!isMouseOverButtonOrMenu() && menu.style.display === "flex") {
|
|
|
|
hideMenu();
|
|
|
|
}
|
2023-11-19 06:05:17 +01:00
|
|
|
|
|
|
|
if (event.target.classList.contains("pfp_character")) {
|
|
|
|
toggleBigPicture();
|
|
|
|
}
|
2023-09-13 20:10:46 +02:00
|
|
|
});
|
|
|
|
|
2023-09-13 19:02:25 +02:00
|
|
|
//------------------------------------------------
|
|
|
|
// Relocate the "Show controls" checkbox
|
|
|
|
//------------------------------------------------
|
2023-10-08 04:07:57 +02:00
|
|
|
var elementToMove = document.getElementById("show-controls");
|
2023-09-13 19:02:25 +02:00
|
|
|
var parent = elementToMove.parentNode;
|
|
|
|
for (var i = 0; i < 2; i++) {
|
|
|
|
parent = parent.parentNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
parent.insertBefore(elementToMove, parent.firstChild);
|
|
|
|
|
2023-09-14 12:06:42 +02:00
|
|
|
//------------------------------------------------
|
2024-12-17 04:47:41 +01:00
|
|
|
// Position the chat input
|
2023-09-14 12:06:42 +02:00
|
|
|
//------------------------------------------------
|
2024-12-17 04:47:41 +01:00
|
|
|
document.getElementById("show-controls").parentNode.classList.add("chat-input-positioned");
|
2023-09-14 12:06:42 +02:00
|
|
|
|
2023-09-13 07:36:12 +02:00
|
|
|
//------------------------------------------------
|
|
|
|
// Focus on the chat input
|
|
|
|
//------------------------------------------------
|
2024-01-10 14:12:49 +01:00
|
|
|
const chatTextArea = document.getElementById("chat-input").querySelector("textarea");
|
|
|
|
|
|
|
|
function respondToChatInputVisibility(element, callback) {
|
|
|
|
var options = {
|
|
|
|
root: document.documentElement,
|
|
|
|
};
|
|
|
|
|
|
|
|
var observer = new IntersectionObserver((entries, observer) => {
|
|
|
|
entries.forEach(entry => {
|
|
|
|
callback(entry.intersectionRatio > 0);
|
|
|
|
});
|
|
|
|
}, options);
|
|
|
|
|
|
|
|
observer.observe(element);
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleChatInputVisibilityChange(isVisible) {
|
|
|
|
if (isVisible) {
|
|
|
|
chatTextArea.focus();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
respondToChatInputVisibility(chatTextArea, handleChatInputVisibilityChange);
|
2023-11-19 06:05:17 +01:00
|
|
|
|
|
|
|
//------------------------------------------------
|
|
|
|
// Show enlarged character picture when the profile
|
|
|
|
// picture is clicked on
|
|
|
|
//------------------------------------------------
|
|
|
|
let bigPictureVisible = false;
|
|
|
|
|
|
|
|
function addBigPicture() {
|
|
|
|
var imgElement = document.createElement("img");
|
|
|
|
var timestamp = new Date().getTime();
|
|
|
|
imgElement.src = "/file/cache/pfp_character.png?time=" + timestamp;
|
|
|
|
imgElement.classList.add("bigProfilePicture");
|
2024-02-25 18:10:16 +01:00
|
|
|
imgElement.addEventListener("load", function () {
|
|
|
|
this.style.visibility = "visible";
|
|
|
|
});
|
|
|
|
imgElement.addEventListener("error", function () {
|
|
|
|
this.style.visibility = "hidden";
|
|
|
|
});
|
2023-11-19 06:05:17 +01:00
|
|
|
|
|
|
|
var imgElementParent = document.getElementById("chat").parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;
|
|
|
|
imgElementParent.appendChild(imgElement);
|
|
|
|
}
|
|
|
|
|
|
|
|
function deleteBigPicture() {
|
2023-12-27 18:59:23 +01:00
|
|
|
var bigProfilePictures = document.querySelectorAll(".bigProfilePicture");
|
2023-11-19 06:05:17 +01:00
|
|
|
bigProfilePictures.forEach(function (element) {
|
|
|
|
element.parentNode.removeChild(element);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function toggleBigPicture() {
|
|
|
|
if(bigPictureVisible) {
|
|
|
|
deleteBigPicture();
|
|
|
|
bigPictureVisible = false;
|
|
|
|
} else {
|
|
|
|
addBigPicture();
|
|
|
|
bigPictureVisible = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-17 02:16:26 +01:00
|
|
|
//------------------------------------------------
|
2024-02-18 04:24:04 +01:00
|
|
|
// Handle the chat input box growth
|
2023-12-17 02:16:26 +01:00
|
|
|
//------------------------------------------------
|
2023-12-22 04:51:20 +01:00
|
|
|
let currentChatInputHeight = 0;
|
|
|
|
|
2024-02-18 04:24:04 +01:00
|
|
|
// Update chat layout based on chat and input dimensions
|
2023-12-21 03:42:23 +01:00
|
|
|
function updateCssProperties() {
|
2023-12-27 18:59:23 +01:00
|
|
|
const chatContainer = document.getElementById("chat").parentNode.parentNode.parentNode;
|
|
|
|
const chatInputHeight = document.querySelector("#chat-input textarea").clientHeight;
|
2024-02-18 04:24:04 +01:00
|
|
|
|
|
|
|
// Check if the chat container is visible
|
2023-12-27 20:51:55 +01:00
|
|
|
if (chatContainer.clientHeight > 0) {
|
2024-12-17 04:47:41 +01:00
|
|
|
const newChatHeight = `${chatContainer.parentNode.clientHeight - chatInputHeight + 40 - 100 - 20}px`;
|
2023-12-27 20:51:55 +01:00
|
|
|
document.documentElement.style.setProperty("--chat-height", newChatHeight);
|
|
|
|
document.documentElement.style.setProperty("--input-delta", `${chatInputHeight - 40}px`);
|
|
|
|
|
2024-02-18 04:24:04 +01:00
|
|
|
// Adjust scrollTop based on input height change
|
2023-12-27 20:51:55 +01:00
|
|
|
if (chatInputHeight !== currentChatInputHeight) {
|
2024-07-27 07:35:30 +02:00
|
|
|
if (!isScrolled && chatInputHeight < currentChatInputHeight) {
|
|
|
|
chatContainer.scrollTop = chatContainer.scrollHeight;
|
|
|
|
} else {
|
|
|
|
chatContainer.scrollTop += chatInputHeight - currentChatInputHeight;
|
|
|
|
}
|
|
|
|
|
2023-12-27 20:51:55 +01:00
|
|
|
currentChatInputHeight = chatInputHeight;
|
|
|
|
}
|
2023-12-22 04:51:20 +01:00
|
|
|
}
|
2023-12-17 02:16:26 +01:00
|
|
|
}
|
|
|
|
|
2024-02-18 04:24:04 +01:00
|
|
|
// Observe textarea size changes and call update function
|
2024-07-22 06:20:22 +02:00
|
|
|
new ResizeObserver(updateCssProperties).observe(document.querySelector("#chat-input textarea"));
|
2023-12-22 04:51:20 +01:00
|
|
|
|
2024-02-18 04:24:04 +01:00
|
|
|
// Handle changes in window size
|
2023-12-27 18:59:23 +01:00
|
|
|
window.addEventListener("resize", updateCssProperties);
|
2024-01-09 05:57:29 +01:00
|
|
|
|
2024-06-26 03:28:58 +02:00
|
|
|
//------------------------------------------------
|
|
|
|
// Focus on the rename text area when it becomes visible
|
|
|
|
//------------------------------------------------
|
|
|
|
const renameTextArea = document.getElementById("rename-row").querySelector("textarea");
|
|
|
|
|
|
|
|
function respondToRenameVisibility(element, callback) {
|
|
|
|
var options = {
|
|
|
|
root: document.documentElement,
|
|
|
|
};
|
|
|
|
|
|
|
|
var observer = new IntersectionObserver((entries, observer) => {
|
|
|
|
entries.forEach(entry => {
|
|
|
|
callback(entry.intersectionRatio > 0);
|
|
|
|
});
|
|
|
|
}, options);
|
|
|
|
|
|
|
|
observer.observe(element);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleVisibilityChange(isVisible) {
|
|
|
|
if (isVisible) {
|
|
|
|
renameTextArea.focus();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
respondToRenameVisibility(renameTextArea, handleVisibilityChange);
|
|
|
|
|
2024-04-06 21:43:21 +02:00
|
|
|
//------------------------------------------------
|
|
|
|
// Adjust the chat tab margin if no extension UI
|
|
|
|
// is present at the bottom
|
|
|
|
//------------------------------------------------
|
|
|
|
|
2024-04-26 15:13:11 +02:00
|
|
|
if (document.getElementById("extensions") === null) {
|
2024-04-06 21:43:21 +02:00
|
|
|
document.getElementById("chat-tab").style.marginBottom = "-29px";
|
|
|
|
}
|
2024-04-06 21:57:57 +02:00
|
|
|
|
|
|
|
//------------------------------------------------
|
|
|
|
// Focus on the chat input after starting a new chat
|
|
|
|
//------------------------------------------------
|
|
|
|
|
2024-04-26 15:13:11 +02:00
|
|
|
document.querySelectorAll(".focus-on-chat-input").forEach(element => {
|
|
|
|
element.addEventListener("click", function() {
|
|
|
|
document.querySelector("#chat-input textarea").focus();
|
2024-04-06 21:57:57 +02:00
|
|
|
});
|
|
|
|
});
|
2024-06-25 05:07:22 +02:00
|
|
|
|
|
|
|
//------------------------------------------------
|
|
|
|
// Fix a border around the "past chats" menu
|
|
|
|
//------------------------------------------------
|
|
|
|
document.getElementById("past-chats").parentNode.style.borderRadius = "0px";
|
2024-06-29 06:20:27 +02:00
|
|
|
|
|
|
|
//------------------------------------------------
|
|
|
|
// Allow the character dropdown to coexist at the
|
|
|
|
// Chat tab and the Parameters > Character tab
|
|
|
|
//------------------------------------------------
|
|
|
|
|
|
|
|
const headerBar = document.querySelector(".header_bar");
|
|
|
|
let originalParent;
|
|
|
|
let originalIndex; // To keep track of the original position
|
|
|
|
let movedElement;
|
|
|
|
|
|
|
|
function moveToChatTab() {
|
|
|
|
const characterMenu = document.getElementById("character-menu");
|
|
|
|
const grandParent = characterMenu.parentElement.parentElement;
|
|
|
|
|
2024-06-29 07:11:31 +02:00
|
|
|
// Save the initial location for the character dropdown
|
2024-06-29 06:20:27 +02:00
|
|
|
if (!originalParent) {
|
|
|
|
originalParent = grandParent.parentElement;
|
|
|
|
originalIndex = Array.from(originalParent.children).indexOf(grandParent);
|
|
|
|
movedElement = grandParent;
|
|
|
|
}
|
|
|
|
|
2024-06-29 07:11:31 +02:00
|
|
|
// Do not show the Character dropdown in the Chat tab when "instruct" mode is selected
|
2024-06-29 06:20:27 +02:00
|
|
|
const instructRadio = document.querySelector("#chat-mode input[value=\"instruct\"]");
|
|
|
|
if (instructRadio && instructRadio.checked) {
|
|
|
|
grandParent.style.display = "none";
|
|
|
|
}
|
|
|
|
|
2024-12-17 04:47:41 +01:00
|
|
|
grandParent.children[0].style.minWidth = "100%";
|
|
|
|
|
2024-06-29 06:20:27 +02:00
|
|
|
const chatControlsFirstChild = document.querySelector("#chat-controls").firstElementChild;
|
|
|
|
const newParent = chatControlsFirstChild;
|
|
|
|
let newPosition = newParent.children.length - 2;
|
|
|
|
|
|
|
|
newParent.insertBefore(grandParent, newParent.children[newPosition]);
|
2024-06-29 07:11:31 +02:00
|
|
|
document.getElementById("save-character").style.display = "none";
|
2024-06-29 06:20:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function restoreOriginalPosition() {
|
|
|
|
if (originalParent && movedElement) {
|
|
|
|
if (originalIndex >= originalParent.children.length) {
|
|
|
|
originalParent.appendChild(movedElement);
|
|
|
|
} else {
|
|
|
|
originalParent.insertBefore(movedElement, originalParent.children[originalIndex]);
|
|
|
|
}
|
|
|
|
|
2024-06-29 07:11:31 +02:00
|
|
|
document.getElementById("save-character").style.display = "";
|
2024-06-29 06:20:27 +02:00
|
|
|
movedElement.style.display = "";
|
2024-12-17 04:47:41 +01:00
|
|
|
movedElement.children[0].style.minWidth = "";
|
2024-06-29 06:20:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
headerBar.addEventListener("click", (e) => {
|
|
|
|
if (e.target.tagName === "BUTTON") {
|
|
|
|
const tabName = e.target.textContent.trim();
|
|
|
|
if (tabName === "Chat") {
|
|
|
|
moveToChatTab();
|
|
|
|
} else {
|
|
|
|
restoreOriginalPosition();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-09-29 06:14:19 +02:00
|
|
|
//------------------------------------------------
|
|
|
|
// Add a confirmation dialog when leaving the page
|
|
|
|
// Useful to avoid data loss
|
|
|
|
//------------------------------------------------
|
2024-10-01 19:22:57 +02:00
|
|
|
window.addEventListener("beforeunload", function (event) {
|
2024-09-29 06:14:19 +02:00
|
|
|
// Cancel the event
|
|
|
|
event.preventDefault();
|
|
|
|
// Chrome requires returnValue to be set
|
2024-10-01 19:22:57 +02:00
|
|
|
event.returnValue = "";
|
2024-09-29 06:14:19 +02:00
|
|
|
});
|
|
|
|
|
2024-06-29 06:20:27 +02:00
|
|
|
moveToChatTab();
|
2024-12-17 04:47:41 +01:00
|
|
|
|
|
|
|
//------------------------------------------------
|
|
|
|
// Buttons to toggle the sidebars
|
|
|
|
//------------------------------------------------
|
|
|
|
|
|
|
|
const leftArrowSVG = `
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-arrow-bar-left">
|
|
|
|
<path d="M4 12l10 0"></path>
|
|
|
|
<path d="M4 12l4 4"></path>
|
|
|
|
<path d="M4 12l4 -4"></path>
|
|
|
|
<path d="M20 4l0 16"></path>
|
|
|
|
</svg>`;
|
|
|
|
|
|
|
|
const rightArrowSVG = `
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-arrow-bar-right">
|
|
|
|
<path d="M20 12l-10 0"></path>
|
|
|
|
<path d="M20 12l-4 4"></path>
|
|
|
|
<path d="M20 12l-4 -4"></path>
|
|
|
|
<path d="M4 4l0 16"></path>
|
|
|
|
</svg>`;
|
|
|
|
|
|
|
|
const hamburgerMenuSVG = `
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-hamburger-menu">
|
|
|
|
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|
|
|
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
|
|
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|
|
|
</svg>`;
|
|
|
|
|
|
|
|
const closeMenuSVG = `
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-close-menu">
|
|
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
|
|
</svg>`;
|
|
|
|
|
|
|
|
const chatTab = document.getElementById("chat-tab");
|
|
|
|
const pastChatsRow = document.getElementById("past-chats-row");
|
|
|
|
const chatControlsRow = document.getElementById("chat-controls");
|
|
|
|
|
|
|
|
if (chatTab) {
|
|
|
|
// Create past-chats-toggle div
|
|
|
|
const pastChatsToggle = document.createElement("div");
|
|
|
|
pastChatsToggle.id = "past-chats-toggle";
|
|
|
|
pastChatsToggle.innerHTML = leftArrowSVG; // Set initial icon to left arrow
|
|
|
|
pastChatsToggle.classList.add("past-chats-open"); // Set initial position
|
|
|
|
|
|
|
|
// Create chat-controls-toggle div
|
|
|
|
const chatControlsToggle = document.createElement("div");
|
|
|
|
chatControlsToggle.id = "chat-controls-toggle";
|
|
|
|
chatControlsToggle.innerHTML = rightArrowSVG; // Set initial icon to right arrow
|
|
|
|
chatControlsToggle.classList.add("chat-controls-open"); // Set initial position
|
|
|
|
|
|
|
|
// Append both elements to the chat-tab
|
|
|
|
chatTab.appendChild(pastChatsToggle);
|
|
|
|
chatTab.appendChild(chatControlsToggle);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create navigation toggle div
|
|
|
|
const navigationToggle = document.createElement("div");
|
|
|
|
navigationToggle.id = "navigation-toggle";
|
|
|
|
navigationToggle.innerHTML = leftArrowSVG; // Set initial icon to right arrow
|
|
|
|
navigationToggle.classList.add("navigation-left"); // Set initial position
|
|
|
|
headerBar.appendChild(navigationToggle);
|
|
|
|
|
|
|
|
// Retrieve the dynamically created toggle buttons
|
|
|
|
const pastChatsToggle = document.getElementById("past-chats-toggle");
|
|
|
|
const chatControlsToggle = document.getElementById("chat-controls-toggle");
|
|
|
|
|
|
|
|
function toggleSidebar(sidebar, toggle) {
|
|
|
|
const isCurrentlyHidden = sidebar.classList.contains("sidebar-hidden");
|
|
|
|
const shouldClose = !isCurrentlyHidden;
|
|
|
|
|
|
|
|
// Apply visibility classes
|
|
|
|
sidebar.classList.toggle("sidebar-hidden", shouldClose);
|
|
|
|
sidebar.classList.toggle("sidebar-shown", !shouldClose);
|
|
|
|
|
|
|
|
if (sidebar === headerBar) {
|
|
|
|
// Special handling for header bar
|
|
|
|
document.documentElement.style.setProperty("--header-width", shouldClose ? "0px" : "112px");
|
|
|
|
pastChatsRow.classList.toggle("negative-header", shouldClose);
|
|
|
|
pastChatsToggle.classList.toggle("negative-header", shouldClose);
|
|
|
|
toggle.innerHTML = shouldClose ? hamburgerMenuSVG : closeMenuSVG;
|
|
|
|
} else if (sidebar === pastChatsRow) {
|
|
|
|
// Past chats sidebar
|
|
|
|
toggle.classList.toggle("past-chats-closed", shouldClose);
|
|
|
|
toggle.classList.toggle("past-chats-open", !shouldClose);
|
|
|
|
toggle.innerHTML = shouldClose ? rightArrowSVG : leftArrowSVG;
|
|
|
|
} else if (sidebar === chatControlsRow) {
|
|
|
|
// Chat controls sidebar
|
|
|
|
toggle.classList.toggle("chat-controls-closed", shouldClose);
|
|
|
|
toggle.classList.toggle("chat-controls-open", !shouldClose);
|
|
|
|
toggle.innerHTML = shouldClose ? leftArrowSVG : rightArrowSVG;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mobile handling
|
|
|
|
if (isMobile()) {
|
|
|
|
sidebar.classList.toggle("sidebar-shown", !shouldClose);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function to check if the device is mobile
|
|
|
|
function isMobile() {
|
|
|
|
return window.innerWidth <= 924;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function to initialize sidebars
|
|
|
|
function initializeSidebars() {
|
|
|
|
const isOnMobile = isMobile();
|
|
|
|
|
|
|
|
if (isOnMobile) {
|
|
|
|
// Mobile state: Hide sidebars and set closed states
|
|
|
|
[pastChatsRow, chatControlsRow, headerBar].forEach(el => {
|
|
|
|
el.classList.add("sidebar-hidden");
|
|
|
|
el.classList.remove("sidebar-shown");
|
|
|
|
});
|
|
|
|
|
|
|
|
document.documentElement.style.setProperty("--header-width", "0px");
|
|
|
|
pastChatsRow.classList.add("negative-header");
|
|
|
|
pastChatsToggle.classList.add("negative-header", "past-chats-closed");
|
|
|
|
pastChatsToggle.classList.remove("past-chats-open");
|
|
|
|
|
|
|
|
[chatControlsToggle, navigationToggle].forEach(el => {
|
|
|
|
el.classList.add("chat-controls-closed");
|
|
|
|
el.classList.remove("chat-controls-open");
|
|
|
|
});
|
|
|
|
|
|
|
|
pastChatsToggle.innerHTML = rightArrowSVG;
|
|
|
|
chatControlsToggle.innerHTML = leftArrowSVG;
|
|
|
|
navigationToggle.innerHTML = hamburgerMenuSVG;
|
|
|
|
} else {
|
|
|
|
// Desktop state: Show sidebars and set open states
|
|
|
|
[pastChatsRow, chatControlsRow].forEach(el => {
|
|
|
|
el.classList.remove("sidebar-hidden", "sidebar-shown");
|
|
|
|
});
|
|
|
|
|
|
|
|
pastChatsToggle.classList.add("past-chats-open");
|
|
|
|
pastChatsToggle.classList.remove("past-chats-closed");
|
|
|
|
|
|
|
|
[chatControlsToggle, navigationToggle].forEach(el => {
|
|
|
|
el.classList.add("chat-controls-open");
|
|
|
|
el.classList.remove("chat-controls-closed");
|
|
|
|
});
|
|
|
|
|
|
|
|
pastChatsToggle.innerHTML = leftArrowSVG;
|
|
|
|
chatControlsToggle.innerHTML = rightArrowSVG;
|
|
|
|
navigationToggle.innerHTML = closeMenuSVG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run the initializer when the page loads
|
|
|
|
initializeSidebars();
|
|
|
|
|
|
|
|
// Add click event listeners to toggle buttons
|
|
|
|
pastChatsToggle.addEventListener("click", () => {
|
|
|
|
toggleSidebar(pastChatsRow, pastChatsToggle);
|
|
|
|
});
|
|
|
|
|
|
|
|
chatControlsToggle.addEventListener("click", () => {
|
|
|
|
toggleSidebar(chatControlsRow, chatControlsToggle);
|
|
|
|
});
|
|
|
|
|
|
|
|
navigationToggle.addEventListener("click", () => {
|
|
|
|
toggleSidebar(headerBar, navigationToggle);
|
|
|
|
});
|
|
|
|
|
|
|
|
//------------------------------------------------
|
|
|
|
// Fixes #chat-input textarea height issue
|
|
|
|
// for devices with width <= 924px
|
|
|
|
//------------------------------------------------
|
|
|
|
|
|
|
|
if (isMobile()) {
|
|
|
|
// Target the textarea
|
|
|
|
const textarea = document.querySelector("#chat-input textarea");
|
|
|
|
|
|
|
|
if (textarea) {
|
|
|
|
// Simulate adding and removing a newline
|
|
|
|
textarea.value += "\n";
|
|
|
|
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
|
|
textarea.value = textarea.value.slice(0, -1);
|
|
|
|
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------
|
|
|
|
// Create a top navigation bar on mobile
|
|
|
|
//------------------------------------------------
|
|
|
|
|
|
|
|
function createMobileTopBar() {
|
|
|
|
const chatTab = document.getElementById("chat-tab");
|
|
|
|
|
|
|
|
// Only create the top bar if it doesn't already exist
|
|
|
|
if (chatTab && !chatTab.querySelector(".mobile-top-bar")) {
|
|
|
|
const topBar = document.createElement("div");
|
|
|
|
topBar.classList.add("mobile-top-bar");
|
|
|
|
|
|
|
|
// Insert the top bar as the first child of chat-tab
|
|
|
|
chatTab.appendChild(topBar);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
createMobileTopBar();
|