Character.AI Text Color

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

当前为 2024-06-27 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==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();