Character.AI Text Color

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

目前為 2024-06-27 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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();