Character.AI Text Color

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

目前为 2024-06-27 提交的版本。查看 最新版本

// ==UserScript==
// @name        Character.AI Text Color 
// @namespace   Character.AI Text Color
// @match       https://old.character.ai/*
// @grant       none
// @license     MIT
// @version     2.4
// @author      Vishanka, K.P. & 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 defaultColor = '#958C7F';
  var color = plaintextColor || defaultColor;

  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");
    for (let j = 0; j < aTags.length; j++) {
      const aTag = aTags[j];
      text = text.replace(aTag.outerHTML, "REPLACE_ME_" + j);
    }

    text = text.replace(/(["“”«»].*?["“”«»])/g, `<span style="color: ${localStorage.getItem('quotationmarks_color') || '#FFFFFF'}">$1</span>`);
    text = text.replace(/<em>(.*?)<\/em>/g,`<span style="color: ${localStorage.getItem('italic_color') || '#E0DF7F'}; font-style: italic;">$1</span>`);

    var wordlist_cc = JSON.parse(localStorage.getItem('wordlist_cc')) || [];
    if (wordlist_cc.length > 0) {
      var wordRegex = new RegExp('\\b(' + wordlist_cc.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|') + ')\\b', 'gi');
      text = text.replace(wordRegex, `<span style="color: ${localStorage.getItem('custom_color') || '#FFFFFF'}">$1</span>`);
    }

    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 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.borderRadius = '5px';
  panel.style.padding = '20px';
  panel.style.zIndex = '9999';

  const categories = ['italic', 'quotationmarks', 'plaintext', 'custom'];
  const colorPickers = {};
  const labelWidth = '150px';

  categories.forEach(category => {
    const colorPicker = document.createElement('input');
    colorPicker.type = 'color';
    const storedColor = localStorage.getItem(`${category}_color`);
    if (storedColor) {
      colorPicker.value = storedColor;
    }

    colorPickers[category] = colorPicker;

    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';

    colorDiv.addEventListener('click', function () {
      colorPicker.click();
    });

    colorPicker.addEventListener('input', function () {
      colorDiv.style.backgroundColor = colorPicker.value;
    });

    const label = document.createElement('label');
    label.style.width = labelWidth;
    label.appendChild(document.createTextNode(`${category}: `));

    const resetButton = createButton('↺', function () {
      colorPicker.value = getDefaultColor(category);
      colorDiv.style.backgroundColor = colorPicker.value;
    });
    resetButton.style.position = 'relative';
    resetButton.style.top = '1px';

    const containerDiv = document.createElement('div');
    containerDiv.appendChild(label);
    containerDiv.appendChild(colorDiv);
    containerDiv.appendChild(resetButton);

    panel.appendChild(containerDiv);
    panel.appendChild(document.createElement('br'));
  });

  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';

  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() {
      const index = wordListArray.indexOf(word);
      if (index !== -1) {
        wordListArray.splice(index, 1);
        updateWordListButtons();
      }
    });

    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 = '';
    wordListArray.forEach(word => {
      const wordButton = createWordButton(word);
      wordListContainer.appendChild(wordButton);
    });
  }

  updateWordListButtons();

  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() {
    const wordListValue = wordListInput.value;
    const newWords = wordListValue.split(',').map(word => word.trim().toLowerCase()).filter(word => word !== '');
    wordListArray.push(...newWords);
    updateWordListButtons();
  });

  const inputButtonContainer = document.createElement('div');
  inputButtonContainer.style.display = 'flex';
  inputButtonContainer.style.alignItems = 'center';

  inputButtonContainer.appendChild(wordListInput);
  inputButtonContainer.appendChild(addWordsButton);
  panel.appendChild(inputButtonContainer);
  panel.appendChild(wordListContainer);
  updateWordListButtons();

  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.addEventListener('click', function() {
    categories.forEach(category => {
      const oldValue = localStorage.getItem(`${category}_color`);
      const newValue = colorPickers[category].value;

      if (oldValue !== newValue) {
        localStorage.setItem(`${category}_color`, newValue);
        if (category === 'plaintext') {
          window.location.reload();
        }
      }
    });

    const wordListValue = wordListInput.value;
    const newWords = wordListValue.split(',').map(word => word.trim().toLowerCase()).filter(word => word !== '');
    const uniqueNewWords = Array.from(new Set(newWords));
    uniqueNewWords.forEach(newWord => {
      if (!wordListArray.includes(newWord)) {
        wordListArray.push(newWord);
      }
    });

    localStorage.setItem('wordlist_cc', JSON.stringify(wordListArray));
    updateWordListButtons();
    panel.remove();
  });

  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() {
    panel.remove();
  });

  panel.appendChild(document.createElement('br'));
  panel.appendChild(okButton);
  panel.appendChild(cancelButton);

  document.body.appendChild(panel);
}

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();
  }
});

mainButton.style.backgroundImage = "url('https://i.imgur.com/yBgJ3za.png')";
mainButton.style.backgroundSize = "cover";
mainButton.style.top = "0px";
mainButton.style.width = "22px";
mainButton.style.height = "22px";

function insertMainButton() {
  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) {
    if (!document.querySelector('.color-palette-button')) {
      mainButton.classList.add('color-palette-button');
      targetPanel1.insertBefore(mainButton, targetPanel1.firstChild);
    }
  } else {
    const observer = new MutationObserver(() => {
      const updatedTargetPanel = document.querySelector(targetSelector);
      if (updatedTargetPanel) {
        mainButton.classList.add('color-palette-button');
        updatedTargetPanel.insertBefore(mainButton, updatedTargetPanel.firstChild);
        observer.disconnect();
      }
    });

    observer.observe(document.body, { subtree: true, childList: true });
    console.error('Target panel not found. Waiting for changes...');
  }
}

// Check periodically if the button is removed and reinsert it
setInterval(insertMainButton, 1000);
insertMainButton();