Character.AI Text Color

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

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

  1. // ==UserScript==
  2. // @name Character.AI Text Color
  3. // @namespace Character.AI Text Color
  4. // @match https://old.character.ai/*
  5. // @grant none
  6. // @license MIT
  7. // @version 2.4
  8. // @author Vishanka, K.P. & ChatGPT
  9. // @description Lets you change the text colors as you wish and highlight chosen words
  10. // @icon https://i.imgur.com/ynjBqKW.png
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. var plaintextColor = localStorage.getItem('plaintext_color');
  15. var defaultColor = '#958C7F';
  16. var color = plaintextColor || defaultColor;
  17.  
  18. var css =
  19. "p, .swiper-no-swiping div { color: " + color + " !important; font-family: 'Noto Sans', sans-serif !important; }";
  20.  
  21. var head = document.getElementsByTagName("head")[0];
  22. var style = document.createElement("style");
  23. style.setAttribute("type", "text/css");
  24. style.innerHTML = css;
  25. head.appendChild(style);
  26. })();
  27.  
  28. function changeColors() {
  29. const pTags = document.getElementsByTagName("p");
  30. for (let i = 0; i < pTags.length; i++) {
  31. const pTag = pTags[i];
  32. if (
  33. pTag.dataset.colorChanged === "true" ||
  34. pTag.querySelector("code") ||
  35. pTag.querySelector("img")
  36. ) {
  37. continue;
  38. }
  39. let text = pTag.innerHTML;
  40.  
  41. const aTags = pTag.getElementsByTagName("a");
  42. for (let j = 0; j < aTags.length; j++) {
  43. const aTag = aTags[j];
  44. text = text.replace(aTag.outerHTML, "REPLACE_ME_" + j);
  45. }
  46.  
  47. text = text.replace(/(["“”«»].*?["“”«»])/g, `<span style="color: ${localStorage.getItem('quotationmarks_color') || '#FFFFFF'}">$1</span>`);
  48. text = text.replace(/<em>(.*?)<\/em>/g,`<span style="color: ${localStorage.getItem('italic_color') || '#E0DF7F'}; font-style: italic;">$1</span>`);
  49.  
  50. var wordlist_cc = JSON.parse(localStorage.getItem('wordlist_cc')) || [];
  51. if (wordlist_cc.length > 0) {
  52. var wordRegex = new RegExp('\\b(' + wordlist_cc.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|') + ')\\b', 'gi');
  53. text = text.replace(wordRegex, `<span style="color: ${localStorage.getItem('custom_color') || '#FFFFFF'}">$1</span>`);
  54. }
  55.  
  56. for (let j = 0; j < aTags.length; j++) {
  57. const aTag = aTags[j];
  58. text = text.replace("REPLACE_ME_" + j, aTag.outerHTML);
  59. }
  60.  
  61. pTag.innerHTML = text;
  62. pTag.dataset.colorChanged = "true";
  63. }
  64.  
  65. console.log("Changed colors");
  66. }
  67.  
  68. const observer = new MutationObserver(changeColors);
  69. observer.observe(document, { subtree: true, childList: true });
  70. changeColors();
  71.  
  72. function createButton(symbol, onClick) {
  73. const colorpalettebutton = document.createElement('button');
  74. colorpalettebutton.innerHTML = symbol;
  75. colorpalettebutton.style.position = 'relative';
  76. colorpalettebutton.style.background = 'none';
  77. colorpalettebutton.style.border = 'none';
  78. colorpalettebutton.style.fontSize = '18px';
  79. colorpalettebutton.style.top = '-5px';
  80. colorpalettebutton.style.cursor = 'pointer';
  81. colorpalettebutton.addEventListener('click', onClick);
  82. return colorpalettebutton;
  83. }
  84.  
  85. function createColorPanel() {
  86. const panel = document.createElement('div');
  87. panel.id = 'colorPanel';
  88. panel.style.position = 'fixed';
  89. panel.style.top = '50%';
  90. panel.style.left = '50%';
  91. panel.style.transform = 'translate(-50%, -50%)';
  92. panel.style.backgroundColor = 'rgba(0, 0, 0, 0.95)';
  93. panel.style.borderRadius = '5px';
  94. panel.style.padding = '20px';
  95. panel.style.zIndex = '9999';
  96.  
  97. const categories = ['italic', 'quotationmarks', 'plaintext', 'custom'];
  98. const colorPickers = {};
  99. const labelWidth = '150px';
  100.  
  101. categories.forEach(category => {
  102. const colorPicker = document.createElement('input');
  103. colorPicker.type = 'color';
  104. const storedColor = localStorage.getItem(`${category}_color`);
  105. if (storedColor) {
  106. colorPicker.value = storedColor;
  107. }
  108.  
  109. colorPickers[category] = colorPicker;
  110.  
  111. const colorDiv = document.createElement('div');
  112. colorDiv.style.position = 'relative';
  113. colorDiv.style.width = '20px';
  114. colorDiv.style.height = '20px';
  115. colorDiv.style.marginLeft = '10px';
  116. colorDiv.style.top = '5px';
  117. colorDiv.style.backgroundColor = colorPicker.value;
  118. colorDiv.style.display = 'inline-block';
  119. colorDiv.style.marginRight = '10px';
  120. colorDiv.style.cursor = 'pointer';
  121. colorDiv.style.border = '1px solid black';
  122.  
  123. colorDiv.addEventListener('click', function () {
  124. colorPicker.click();
  125. });
  126.  
  127. colorPicker.addEventListener('input', function () {
  128. colorDiv.style.backgroundColor = colorPicker.value;
  129. });
  130.  
  131. const label = document.createElement('label');
  132. label.style.width = labelWidth;
  133. label.appendChild(document.createTextNode(`${category}: `));
  134.  
  135. const resetButton = createButton('↺', function () {
  136. colorPicker.value = getDefaultColor(category);
  137. colorDiv.style.backgroundColor = colorPicker.value;
  138. });
  139. resetButton.style.position = 'relative';
  140. resetButton.style.top = '1px';
  141.  
  142. const containerDiv = document.createElement('div');
  143. containerDiv.appendChild(label);
  144. containerDiv.appendChild(colorDiv);
  145. containerDiv.appendChild(resetButton);
  146.  
  147. panel.appendChild(containerDiv);
  148. panel.appendChild(document.createElement('br'));
  149. });
  150.  
  151. const wordListInput = document.createElement('input');
  152. wordListInput.type = 'text';
  153. wordListInput.placeholder = 'Separate words with commas';
  154. wordListInput.style.width = '250px';
  155. wordListInput.style.height = '35px';
  156. wordListInput.style.borderRadius = '3px';
  157. wordListInput.style.marginBottom = '10px';
  158. panel.appendChild(wordListInput);
  159. panel.appendChild(document.createElement('br'));
  160.  
  161. const wordListContainer = document.createElement('div');
  162. wordListContainer.style.display = 'flex';
  163. wordListContainer.style.flexWrap = 'wrap';
  164. wordListContainer.style.maxWidth = '300px';
  165.  
  166. const wordListArray = JSON.parse(localStorage.getItem('wordlist_cc')) || [];
  167. const wordListButtons = [];
  168.  
  169. function createWordButton(word) {
  170. const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
  171. const removeSymbol = isMobile ? '×' : '🞮';
  172.  
  173. const wordButton = createButton(`${word} ${removeSymbol}`, function() {
  174. const index = wordListArray.indexOf(word);
  175. if (index !== -1) {
  176. wordListArray.splice(index, 1);
  177. updateWordListButtons();
  178. }
  179. });
  180.  
  181. wordButton.style.borderRadius = '3px';
  182. wordButton.style.border = 'none';
  183. wordButton.style.backgroundColor = 'lightsteelblue';
  184. wordButton.style.marginBottom = '5px';
  185. wordButton.style.marginRight = '5px';
  186. wordButton.style.fontSize = '16px';
  187. wordButton.classList.add('word-button');
  188. return wordButton;
  189. }
  190.  
  191. function updateWordListButtons() {
  192. wordListContainer.innerHTML = '';
  193. wordListArray.forEach(word => {
  194. const wordButton = createWordButton(word);
  195. wordListContainer.appendChild(wordButton);
  196. });
  197. }
  198.  
  199. updateWordListButtons();
  200.  
  201. const addWordsButton = document.createElement('button');
  202. addWordsButton.textContent = 'Add';
  203. addWordsButton.style.marginTop = '-8px';
  204. addWordsButton.style.marginLeft = '5px';
  205. addWordsButton.style.borderRadius = '3px';
  206. addWordsButton.style.border = 'none';
  207. addWordsButton.style.backgroundColor = 'lightsteelblue';
  208. addWordsButton.addEventListener('click', function() {
  209. const wordListValue = wordListInput.value;
  210. const newWords = wordListValue.split(',').map(word => word.trim().toLowerCase()).filter(word => word !== '');
  211. wordListArray.push(...newWords);
  212. updateWordListButtons();
  213. });
  214.  
  215. const inputButtonContainer = document.createElement('div');
  216. inputButtonContainer.style.display = 'flex';
  217. inputButtonContainer.style.alignItems = 'center';
  218.  
  219. inputButtonContainer.appendChild(wordListInput);
  220. inputButtonContainer.appendChild(addWordsButton);
  221. panel.appendChild(inputButtonContainer);
  222. panel.appendChild(wordListContainer);
  223. updateWordListButtons();
  224.  
  225. const okButton = document.createElement('button');
  226. okButton.textContent = 'Confirm';
  227. okButton.style.marginTop = '-20px';
  228. okButton.style.width = '75px';
  229. okButton.style.height = '35px';
  230. okButton.style.marginRight = '5px';
  231. okButton.style.borderRadius = '3px';
  232. okButton.style.border = 'none';
  233. okButton.style.backgroundColor = 'lightsteelblue';
  234. okButton.style.position = 'relative';
  235. okButton.style.left = '24%';
  236. okButton.addEventListener('click', function() {
  237. categories.forEach(category => {
  238. const oldValue = localStorage.getItem(`${category}_color`);
  239. const newValue = colorPickers[category].value;
  240.  
  241. if (oldValue !== newValue) {
  242. localStorage.setItem(`${category}_color`, newValue);
  243. if (category === 'plaintext') {
  244. window.location.reload();
  245. }
  246. }
  247. });
  248.  
  249. const wordListValue = wordListInput.value;
  250. const newWords = wordListValue.split(',').map(word => word.trim().toLowerCase()).filter(word => word !== '');
  251. const uniqueNewWords = Array.from(new Set(newWords));
  252. uniqueNewWords.forEach(newWord => {
  253. if (!wordListArray.includes(newWord)) {
  254. wordListArray.push(newWord);
  255. }
  256. });
  257.  
  258. localStorage.setItem('wordlist_cc', JSON.stringify(wordListArray));
  259. updateWordListButtons();
  260. panel.remove();
  261. });
  262.  
  263. const cancelButton = document.createElement('button');
  264. cancelButton.textContent = 'Cancel';
  265. cancelButton.style.marginTop = '-20px';
  266. cancelButton.style.borderRadius = '3px';
  267. cancelButton.style.width = '75px';
  268. cancelButton.style.marginLeft = '5px';
  269. cancelButton.style.height = '35px';
  270. cancelButton.style.border = 'none';
  271. cancelButton.style.backgroundColor = 'darkgrey';
  272. cancelButton.style.position = 'relative';
  273. cancelButton.style.left = '25%';
  274. cancelButton.addEventListener('click', function() {
  275. panel.remove();
  276. });
  277.  
  278. panel.appendChild(document.createElement('br'));
  279. panel.appendChild(okButton);
  280. panel.appendChild(cancelButton);
  281.  
  282. document.body.appendChild(panel);
  283. }
  284.  
  285. function getDefaultColor(category) {
  286. const defaultColors = {
  287. 'italic': '#E0DF7F',
  288. 'quotationmarks': '#FFFFFF',
  289. 'plaintext': '#958C7F',
  290. 'custom': '#E0DF7F'
  291. };
  292. return defaultColors[category];
  293. }
  294.  
  295. const mainButton = createButton('', function() {
  296. const colorPanelExists = document.getElementById('colorPanel');
  297. if (!colorPanelExists) {
  298. createColorPanel();
  299. }
  300. });
  301.  
  302. mainButton.style.backgroundImage = "url('https://i.imgur.com/yBgJ3za.png')";
  303. mainButton.style.backgroundSize = "cover";
  304. mainButton.style.top = "0px";
  305. mainButton.style.width = "22px";
  306. mainButton.style.height = "22px";
  307.  
  308. function insertMainButton() {
  309. const targetSelector = '.chat2 > div:nth-child(1) > div:nth-child(1) > div:nth-child(3) > div:nth-child(1)';
  310. const targetPanel1 = document.querySelector(targetSelector);
  311.  
  312. if (targetPanel1) {
  313. if (!document.querySelector('.color-palette-button')) {
  314. mainButton.classList.add('color-palette-button');
  315. targetPanel1.insertBefore(mainButton, targetPanel1.firstChild);
  316. }
  317. } else {
  318. const observer = new MutationObserver(() => {
  319. const updatedTargetPanel = document.querySelector(targetSelector);
  320. if (updatedTargetPanel) {
  321. mainButton.classList.add('color-palette-button');
  322. updatedTargetPanel.insertBefore(mainButton, updatedTargetPanel.firstChild);
  323. observer.disconnect();
  324. }
  325. });
  326.  
  327. observer.observe(document.body, { subtree: true, childList: true });
  328. console.error('Target panel not found. Waiting for changes...');
  329. }
  330. }
  331.  
  332. // Check periodically if the button is removed and reinsert it
  333. setInterval(insertMainButton, 1000);
  334. insertMainButton();