c.ai X Text Color

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

当前为 2024-04-05 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        c.ai X Text Color
// @namespace   c.ai X Text Color
// @match       https://character.ai/*
// @grant       none
// @license     MIT
// @version     2.7
// @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');
  var italicColor = localStorage.getItem('italic_color');
  var charbubbleColor = localStorage.getItem('charbubble_color') || '#26272B';
  var userbubbleColor = localStorage.getItem('userbubble_color') || '#303136';
  // Default color if 'plaintext_color' is not set
  var defaultColor = '#A2A2AC';

  // Use the retrieved color or default color
  var color = plaintextColor || defaultColor;

  // Create the CSS style
  var css = "p[node='[object Object]'] { color: " + color + " !important; font-family: '__Inter_918210','Noto Sans', sans-serif !important; } p, textarea, button, div.text-sm { font-family: '__Inter_918210','Noto Sans', sans-serif !important; } em { color: " + italicColor + " !important; }";

 css += `.mt-1.bg-surface-elevation-2 { background-color: ${charbubbleColor}; } .mt-1.bg-surface-elevation-3 { background-color: ${userbubbleColor}; }`;



  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");
  const quotationMarksColor = localStorage.getItem('quotationmarks_color') || '#FFFFFF';
  const customColor = localStorage.getItem('custom_color') || '#FFFFFF';
  const wordlistCc = JSON.parse(localStorage.getItem('wordlist_cc')) || [];

  const wordRegex = wordlistCc.length > 0 ? new RegExp('\\b(' + wordlistCc.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|') + ')\\b', 'gi') : null;

  Array.from(pTags).forEach((pTag) => {
    if (
      pTag.dataset.colorChanged === "true" ||
      pTag.querySelector("code") ||
      pTag.querySelector("img") ||
      pTag.querySelector("textarea") ||
      pTag.querySelector("button") ||
      pTag.querySelector("div")
    ) {
      return; // Skip iteration
    }

    let text = pTag.innerHTML;

    // Save .katex elements' original HTML and replace with placeholders
    const katexElems = Array.from(pTag.querySelectorAll(".katex"));
    const katexReplacements = katexElems.map((elem, index) => {
      const placeholder = `KATEX_PLACEHOLDER_${index}`;
      text = text.replace(elem.outerHTML, placeholder);
      return { html: elem.outerHTML, placeholder };
    });

    // Handle <a> tags by removing them temporarily and saving their HTML for later restoration
    const aTags = Array.from(pTag.getElementsByTagName("a"));
    const aTagsReplacements = aTags.map((aTag, j) => {
      const placeholder = `REPLACE_ME_${j}`;
      text = text.replace(aTag.outerHTML, placeholder);
      return { tag: aTag, placeholder };
    });

    // Change text within quotation marks and for specific words based on the regex
    text = text.replace(/(["“”«»].*?["“”«»])/g, `<span style="color: ${quotationMarksColor}">$1</span>`);
    text = text.replace(/(["“”«»][^"]*?,["“”«»])/g, `<span style="color: #E0DF7F">$1</span>`);

    if (wordRegex) {
      text = text.replace(wordRegex, `<span style="color: ${customColor}">$1</span>`);
    }

    // Restore .katex elements and <a> tags
    [...katexReplacements, ...aTagsReplacements].forEach(({ html, placeholder, tag }) => {
      text = text.replace(placeholder, html || tag.outerHTML);
    });

    // Update the innerHTML and mark the <p> tag to avoid re-processing
    pTag.innerHTML = text;
    pTag.dataset.colorChanged = "true";
  });

  console.log("Changed colors");
}

const divElements = document.querySelectorAll('div');

divElements.forEach(div => {
    const observer = new MutationObserver(changeColors);
    observer.observe(div, { subtree: true, childList: true });
});



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%)';
    const currentTheme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
if (currentTheme === 'dark') {
    panel.style.backgroundColor = 'rgba(19, 19, 22, 0.95)';
} else {
     panel.style.backgroundColor = 'rgba(214, 214, 221, 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', 'charbubble', 'userbubble'];

    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';
if (currentTheme === 'dark') {
    wordButton.style.backgroundColor = '#26272B';
} else {
    wordButton.style.backgroundColor = '#E4E4E7';
}
    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';
if (currentTheme === 'dark') {
addWordsButton.style.backgroundColor = '#26272B';
} else {
addWordsButton.style.backgroundColor = '#E4E4E7';
}
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';
if (currentTheme === 'dark') {
    okButton.style.backgroundColor = '#26272B';
} else {
    okButton.style.backgroundColor = '#D9D9DF';
}

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';
if (currentTheme === 'dark') {
    cancelButton.style.backgroundColor = '#5E5E5E';
} else {
    cancelButton.style.backgroundColor = '#CBD2D4';
}
    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 currentTheme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
if (currentTheme === 'dark') {
    const defaultColors = {
        'italic': '#E0DF7F',
        'quotationmarks': '#FFFFFF',
        'plaintext': '#A2A2AC',
        'custom': '#E0DF7F',
        'charbubble': '#26272B',
        'userbubble': '#303136'
    };
    return defaultColors[category];
}
 else {
    const defaultColors = {
        'italic': '#4F7AA6',
        'quotationmarks': '#000000',
        'plaintext': '#374151',
        'custom': '#4F7AA6',
        'charbubble': '#E4E4E7',
        'userbubble': '#D9D9DF'
};
    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.position = "fixed"; // Use "fixed" for a position relative to the viewport
mainButton.style.top = "10px"; // Adjust the top position as needed
mainButton.style.right = "10px"; // Adjust the right position as needed
mainButton.style.width = "22px";  // Adjust the width and height as needed
mainButton.style.height = "22px"; // Adjust the width and height as needed

// Function to insert the mainButton into the body of the document
function insertMainButton() {
    document.body.appendChild(mainButton);
}

// Call the function to insert the mainButton into the body
insertMainButton();

console.error('Main button appended to the top right corner.');