Character.AI Text Color

Lets you change the text colors as you wish and highlight chosen words

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name        Character.AI Text Color
// @namespace   Character.AI Text Color by Vishanka
// @match       https://*.character.ai/*
// @grant       none
// @license     MIT
// @version     2.3
// @author      Vishanka via chatGPT
// @description Lets you change the text colors as you wish and highlight chosen words
// @icon        https://i.imgur.com/ynjBqKW.png
// ==/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 <a> tags within the <p> tag

    // Remove the <a> 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, `<span style="color: ${localStorage.getItem('quotationmarks_color') || '#FFFFFF'}">$1</span>`);
//Changes Text within Quotation Marks and a comma at the end to yellow
//  text = text.replace(/(["“”«»][^"]*?,["“”«»])/g, '<span style="color: #E0DF7F">$1</span>');
// Changes Italic Text Color
  text = text.replace(/<em>(.*?)<\/em>/g,`<span style="color: ${localStorage.getItem('italic_color') || '#E0DF7F'}; font-style: italic;">$1</span>`);
// Changes Textcolor within Percentages to blue
//  text = text.replace(/([%][^"]*?[%])/g, '<span style="color: #0000FF">$1</span>');

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, `<span style="color: ${localStorage.getItem('custom_color') || '#FFFFFF'}">$1</span>`);
}



    // Restore the <a> 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 isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);

    const removeSymbol = isMobile ? '×' : '🞮';

    const wordButton = createButton(`${word} ${removeSymbol}`, 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().toLowerCase()).filter(word => word !== ''); // Convert to lowercase and remove empty entries
    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...');
}