// ==UserScript== // @name Character.AI Text Color // @namespace Character.AI Text Color by Vishanka // @match https://*.character.ai/* // @grant none // @license MIT // @version 2.1 // @author Vishanka via chatGPT // @description Changes the color of all text except the text within "Quotation Marks" // @icon https://i.imgur.com/ynjBqKW.png // @downloadURL none // ==/UserScript== (function () { var plaintextColor = localStorage.getItem('plaintext_color'); // Default color if 'plaintext_color' is not set var defaultColor = '#958C7F'; // Use the retrieved color or default color var color = plaintextColor || defaultColor; // Create the CSS style var css = "p, .swiper-no-swiping div { color: " + color + " !important; font-family: 'Noto Sans', sans-serif !important; }"; var head = document.getElementsByTagName("head")[0]; var style = document.createElement("style"); style.setAttribute("type", "text/css"); style.innerHTML = css; head.appendChild(style); })(); function changeColors() { const pTags = document.getElementsByTagName("p"); for (let i = 0; i < pTags.length; i++) { const pTag = pTags[i]; if ( pTag.dataset.colorChanged === "true" || pTag.querySelector("code") || pTag.querySelector("img") ) { continue; } let text = pTag.innerHTML; const aTags = pTag.getElementsByTagName("a"); // Get all tags within the

tag // Remove the tags temporarily for (let j = 0; j < aTags.length; j++) { const aTag = aTags[j]; text = text.replace(aTag.outerHTML, "REPLACE_ME_" + j); // Use a placeholder to be able to restore the links later } //Changes Text within Quotation Marks to white text = text.replace(/(["“”«»].*?["“”«»])/g, `$1`); //Changes Text within Quotation Marks and a comma at the end to orange text = text.replace(/(["“”«»][^"]*?,["“”«»])/g, '$1'); // Changes Italic Text Color text = text.replace(/(.*?)<\/em>/g,`$1`); var wordlist_cc = JSON.parse(localStorage.getItem('wordlist_cc')) || []; // Check if the wordlist is not empty before creating the regex if (wordlist_cc.length > 0) { // Escape special characters in each word and join them with the "|" operator var wordRegex = new RegExp('\\b(' + wordlist_cc.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|') + ')\\b', 'gi'); // Replace matching words in the text text = text.replace(wordRegex, `$1`); } // Restore the tags for (let j = 0; j < aTags.length; j++) { const aTag = aTags[j]; text = text.replace("REPLACE_ME_" + j, aTag.outerHTML); } pTag.innerHTML = text; pTag.dataset.colorChanged = "true"; } console.log("Changed colors"); } const observer = new MutationObserver(changeColors); observer.observe(document, { subtree: true, childList: true }); changeColors(); function createButton(symbol, onClick) { const colorpalettebutton = document.createElement('button'); colorpalettebutton.innerHTML = symbol; colorpalettebutton.style.position = 'relative'; colorpalettebutton.style.background = 'none'; colorpalettebutton.style.border = 'none'; colorpalettebutton.style.fontSize = '18px'; colorpalettebutton.style.top = '-5px'; colorpalettebutton.style.cursor = 'pointer'; colorpalettebutton.addEventListener('click', onClick); return colorpalettebutton; } // Function to create the color selector panel function createColorPanel() { const panel = document.createElement('div'); panel.id = 'colorPanel'; panel.style.position = 'fixed'; panel.style.top = '50%'; panel.style.left = '50%'; panel.style.transform = 'translate(-50%, -50%)'; panel.style.backgroundColor = 'rgba(0, 0, 0, 0.95)'; panel.style.border = 'none'; panel.style.borderRadius = '5px'; panel.style.padding = '20px'; // panel.style.border = '2px solid #000'; panel.style.zIndex = '9999'; const categories = ['italic', 'quotationmarks', 'plaintext', 'custom']; const colorPickers = {}; // Set a fixed width for the labels const labelWidth = '150px'; categories.forEach(category => { const colorPicker = document.createElement('input'); colorPicker.type = 'color'; // Retrieve stored color from local storage const storedColor = localStorage.getItem(`${category}_color`); if (storedColor) { colorPicker.value = storedColor; } colorPickers[category] = colorPicker; // Create a div to hold color picker const colorDiv = document.createElement('div'); colorDiv.style.position = 'relative'; colorDiv.style.width = '20px'; colorDiv.style.height = '20px'; colorDiv.style.marginLeft = '10px'; colorDiv.style.top = '5px'; colorDiv.style.backgroundColor = colorPicker.value; colorDiv.style.display = 'inline-block'; colorDiv.style.marginRight = '10px'; colorDiv.style.cursor = 'pointer'; colorDiv.style.border = '1px solid black'; // Event listener to open color picker when the color square is clicked colorDiv.addEventListener('click', function () { colorPicker.click(); }); // Event listener to update the color div when the color changes colorPicker.addEventListener('input', function () { colorDiv.style.backgroundColor = colorPicker.value; }); const label = document.createElement('label'); label.style.width = labelWidth; // Set fixed width for the label label.appendChild(document.createTextNode(`${category}: `)); // Reset button for each color picker const resetButton = createButton('↺', function () { colorPicker.value = getDefaultColor(category); colorDiv.style.backgroundColor = colorPicker.value; }); resetButton.style.position = 'relative'; resetButton.style.top = '1px'; // Create a div to hold label, color picker, and reset button const containerDiv = document.createElement('div'); containerDiv.appendChild(label); containerDiv.appendChild(colorDiv); containerDiv.appendChild(resetButton); panel.appendChild(containerDiv); panel.appendChild(document.createElement('br')); }); // Custom word list input const wordListInput = document.createElement('input'); wordListInput.type = 'text'; wordListInput.placeholder = 'Separate words with commas'; wordListInput.style.width = '250px'; wordListInput.style.height = '35px'; wordListInput.style.borderRadius = '3px'; wordListInput.style.marginBottom = '10px'; panel.appendChild(wordListInput); panel.appendChild(document.createElement('br')); const wordListContainer = document.createElement('div'); wordListContainer.style.display = 'flex'; wordListContainer.style.flexWrap = 'wrap'; wordListContainer.style.maxWidth = '300px'; // Set a fixed maximum width for the container // Display custom word list buttons const wordListArray = JSON.parse(localStorage.getItem('wordlist_cc')) || []; const wordListButtons = []; function createWordButton(word) { const wordButton = createButton(`${word} 🞮`, function() { // Remove the word from the list and update the panel const index = wordListArray.indexOf(word); if (index !== -1) { wordListArray.splice(index, 1); updateWordListButtons(); } }); // Word Buttons wordButton.style.borderRadius = '3px'; wordButton.style.border = 'none'; wordButton.style.backgroundColor = 'lightsteelblue'; wordButton.style.marginBottom = '5px'; wordButton.style.marginRight = '5px'; wordButton.style.fontSize = '16px'; wordButton.classList.add('word-button'); return wordButton; } function updateWordListButtons() { wordListContainer.innerHTML = ''; // Clear the container wordListArray.forEach(word => { const wordButton = createWordButton(word); wordListContainer.appendChild(wordButton); }); } // Append wordListContainer to the panel updateWordListButtons(); // Add Words button const addWordsButton = document.createElement('button'); addWordsButton.textContent = 'Add'; addWordsButton.style.marginTop = '-8px'; addWordsButton.style.marginLeft = '5px'; addWordsButton.style.borderRadius = '3px'; addWordsButton.style.border = 'none'; addWordsButton.style.backgroundColor = 'lightsteelblue'; addWordsButton.addEventListener('click', function() { // Get the input value, split into words, and add to wordListArray const wordListValue = wordListInput.value; const newWords = wordListValue.split(',').map(word => word.trim()); wordListArray.push(...newWords); // Update the word list buttons in the panel updateWordListButtons(); }); // Create a div to group the input and button on the same line const inputButtonContainer = document.createElement('div'); inputButtonContainer.style.display = 'flex'; inputButtonContainer.style.alignItems = 'center'; inputButtonContainer.appendChild(wordListInput); inputButtonContainer.appendChild(addWordsButton); // Append the container to the panel panel.appendChild(inputButtonContainer); panel.appendChild(wordListContainer); // Create initial word list buttons updateWordListButtons(); // OK button const okButton = document.createElement('button'); okButton.textContent = 'Confirm'; okButton.style.marginTop = '-20px'; okButton.style.width = '75px'; okButton.style.height = '35px'; okButton.style.marginRight = '5px'; okButton.style.borderRadius = '3px'; okButton.style.border = 'none'; okButton.style.backgroundColor = 'lightsteelblue'; okButton.style.position = 'relative'; okButton.style.left = '24%'; //okButton.style.transform = 'translateX(-50%)'; okButton.addEventListener('click', function() { // Save selected colors to local storage categories.forEach(category => { const oldValue = localStorage.getItem(`${category}_color`); const newValue = colorPickers[category].value; if (oldValue !== newValue) { localStorage.setItem(`${category}_color`, newValue); // If 'plaintext' color is changed, auto-reload the page if (category === 'plaintext') { window.location.reload(); } } }); // Save custom word list to local storage const wordListValue = wordListInput.value; const newWords = wordListValue.split(',').map(word => word.trim().toLowerCase()).filter(word => word !== ''); // Convert to lowercase and remove empty entries const uniqueNewWords = Array.from(new Set(newWords)); // Remove duplicates // Check for existing words and add only new ones uniqueNewWords.forEach(newWord => { if (!wordListArray.includes(newWord)) { wordListArray.push(newWord); } }); localStorage.setItem('wordlist_cc', JSON.stringify(wordListArray)); updateWordListButtons(); // Close the panel panel.remove(); }); // Cancel button const cancelButton = document.createElement('button'); cancelButton.textContent = 'Cancel'; cancelButton.style.marginTop = '-20px'; cancelButton.style.borderRadius = '3px'; cancelButton.style.width = '75px'; cancelButton.style.marginLeft = '5px'; cancelButton.style.height = '35px'; cancelButton.style.border = 'none'; cancelButton.style.backgroundColor = 'darkgrey'; cancelButton.style.position = 'relative'; cancelButton.style.left = '25%'; cancelButton.addEventListener('click', function() { // Close the panel without saving panel.remove(); }); panel.appendChild(document.createElement('br')); panel.appendChild(okButton); panel.appendChild(cancelButton); document.body.appendChild(panel); } // Function to get the default color for a category function getDefaultColor(category) { const defaultColors = { 'italic': '#E0DF7F', 'quotationmarks': '#FFFFFF', 'plaintext': '#958C7F', 'custom': '#E0DF7F' }; return defaultColors[category]; } const mainButton = createButton('', function() { const colorPanelExists = document.getElementById('colorPanel'); if (!colorPanelExists) { createColorPanel(); } }); // Set the background image of the button to the provided image mainButton.style.backgroundImage = "url('https://i.imgur.com/yBgJ3za.png')"; mainButton.style.backgroundSize = "cover"; mainButton.style.top = "0px"; mainButton.style.width = "22px"; // Adjust the width and height as needed mainButton.style.height = "22px"; // Adjust the width and height as needed const targetSelector = '.chat2 > div:nth-child(1) > div:nth-child(1) > div:nth-child(3) > div:nth-child(1)'; const targetPanel1 = document.querySelector(targetSelector); if (targetPanel1) { targetPanel1.insertBefore(mainButton, targetPanel1.firstChild); } else { // If the target panel is not found, set up a MutationObserver to keep checking for it. const observer = new MutationObserver(() => { const updatedTargetPanel = document.querySelector(targetSelector); if (updatedTargetPanel) { // Once the target panel is available, insert the mainButton and disconnect the observer. updatedTargetPanel.insertBefore(mainButton, updatedTargetPanel.firstChild); observer.disconnect(); } }); // Set up the observer to watch for changes in the parent of the target panel or higher up in the DOM. observer.observe(document.body, { subtree: true, childList: true }); console.error('Target panel not found. Waiting for changes...'); }