Enhanced Character.AI Font and Colors

Enhanced script to change text colors, fonts, and sizes with a modern UI using Tailwind CSS

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

  1. // ==UserScript==
  2. // @name Enhanced Character.AI Font and Colors
  3. // @namespace EnhancedCharacterAITextColor
  4. // @match https://old.character.ai/*
  5. // @grant none
  6. // @license MIT
  7. // @version 1.1
  8. // @description Enhanced script to change text colors, fonts, and sizes with a modern UI using Tailwind CSS
  9. // @icon https://i.imgur.com/ynjBqKW.png
  10. // @author Karan via ChatGPT
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. // Add Tailwind CSS to the document
  15. const tailwindCSS = document.createElement('link');
  16. tailwindCSS.href = 'https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css';
  17. tailwindCSS.rel = 'stylesheet';
  18. document.head.appendChild(tailwindCSS);
  19.  
  20. var plaintextColor = localStorage.getItem('plaintext_color') || '#958C7F';
  21. var fontFamily = localStorage.getItem('font_family') || 'Noto Sans, sans-serif';
  22. var fontSize = localStorage.getItem('font_size') || '16px';
  23.  
  24. var css = `
  25. p, .swiper-no-swiping div {
  26. color: ${plaintextColor} !important;
  27. font-family: ${fontFamily} !important;
  28. font-size: ${fontSize} !important;
  29. }
  30. `;
  31.  
  32. var head = document.getElementsByTagName("head")[0];
  33. var style = document.createElement("style");
  34. style.setAttribute("type", "text/css");
  35. style.innerHTML = css;
  36. head.appendChild(style);
  37. })();
  38.  
  39. function changeColors() {
  40. const pTags = document.getElementsByTagName("p");
  41. for (let i = 0; i < pTags.length; i++) {
  42. const pTag = pTags[i];
  43. if (
  44. pTag.dataset.colorChanged === "true" ||
  45. pTag.querySelector("code") ||
  46. pTag.querySelector("img")
  47. ) {
  48. continue;
  49. }
  50. let text = pTag.innerHTML;
  51.  
  52. const aTags = pTag.getElementsByTagName("a");
  53. for (let j = 0; j < aTags.length; j++) {
  54. const aTag = aTags[j];
  55. text = text.replace(aTag.outerHTML, "REPLACE_ME_" + j);
  56. }
  57.  
  58. text = text.replace(/(["“”«»].*?["“”«»])/g, `<span style="color: ${localStorage.getItem('quotationmarks_color') || '#FFFFFF'}">$1</span>`);
  59. text = text.replace(/<em>(.*?)<\/em>/g,`<span style="color: ${localStorage.getItem('italic_color') || '#E0DF7F'}; font-style: italic;">$1</span>`);
  60.  
  61. var wordlist_cc = JSON.parse(localStorage.getItem('wordlist_cc')) || [];
  62. if (wordlist_cc.length > 0) {
  63. var wordRegex = new RegExp('\\b(' + wordlist_cc.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|') + ')\\b', 'gi');
  64. text = text.replace(wordRegex, `<span style="color: ${localStorage.getItem('custom_color') || '#FFFFFF'}">$1</span>`);
  65. }
  66.  
  67. for (let j = 0; j < aTags.length; j++) {
  68. const aTag = aTags[j];
  69. text = text.replace("REPLACE_ME_" + j, aTag.outerHTML);
  70. }
  71.  
  72. pTag.innerHTML = text;
  73. pTag.dataset.colorChanged = "true";
  74. }
  75.  
  76. console.log("Changed colors");
  77. }
  78.  
  79. const observer = new MutationObserver(changeColors);
  80. observer.observe(document, { subtree: true, childList: true });
  81. changeColors();
  82.  
  83. function createButton(symbol, onClick, extraClasses = '') {
  84. const button = document.createElement('button');
  85. button.innerHTML = symbol;
  86. button.className = `relative bg-none border-none text-lg cursor-pointer ${extraClasses}`;
  87. button.addEventListener('click', onClick);
  88. return button;
  89. }
  90.  
  91. function createColorPanel() {
  92. const panel = document.createElement('div');
  93. panel.id = 'colorPanel';
  94. panel.className = 'fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-gray-800 rounded-lg p-6 shadow-lg text-white z-50';
  95.  
  96. const categories = ['italic', 'quotationmarks', 'plaintext', 'custom'];
  97. const colorPickers = {};
  98.  
  99. categories.forEach(category => {
  100. const colorPicker = document.createElement('input');
  101. colorPicker.type = 'color';
  102. const storedColor = localStorage.getItem(`${category}_color`);
  103. if (storedColor) {
  104. colorPicker.value = storedColor;
  105. }
  106.  
  107. colorPickers[category] = colorPicker;
  108.  
  109. const colorDiv = document.createElement('div');
  110. colorDiv.className = 'relative w-5 h-5 ml-2 mt-1 bg-current inline-block mr-2 cursor-pointer border border-black';
  111. colorDiv.style.backgroundColor = colorPicker.value;
  112.  
  113. colorDiv.addEventListener('click', function () {
  114. colorPicker.click();
  115. });
  116.  
  117. colorPicker.addEventListener('input', function () {
  118. colorDiv.style.backgroundColor = colorPicker.value;
  119. });
  120.  
  121. const label = document.createElement('label');
  122. label.className = 'inline-block w-32';
  123. label.appendChild(document.createTextNode(`${category}: `));
  124.  
  125. const resetButton = createButton('↺', function () {
  126. colorPicker.value = getDefaultColor(category);
  127. colorDiv.style.backgroundColor = colorPicker.value;
  128. }, 'relative top-1');
  129.  
  130. const containerDiv = document.createElement('div');
  131. containerDiv.appendChild(label);
  132. containerDiv.appendChild(colorDiv);
  133. containerDiv.appendChild(resetButton);
  134.  
  135. panel.appendChild(containerDiv);
  136. panel.appendChild(document.createElement('br'));
  137. });
  138.  
  139. // Font selector
  140. const fontSelector = document.createElement('select');
  141. fontSelector.className = 'mt-4 bg-gray-700 border-none text-white rounded p-2';
  142. const fonts = ['Noto Sans', 'Arial', 'Times New Roman', 'Courier New', 'Verdana'];
  143. fonts.forEach(font => {
  144. const option = document.createElement('option');
  145. option.value = font;
  146. option.textContent = font;
  147. fontSelector.appendChild(option);
  148. });
  149. fontSelector.value = localStorage.getItem('font_family') || 'Noto Sans';
  150.  
  151. const fontSizeInput = document.createElement('input');
  152. fontSizeInput.type = 'number';
  153. fontSizeInput.className = 'mt-4 bg-gray-700 border-none text-white rounded p-2 w-20';
  154. fontSizeInput.value = localStorage.getItem('font_size') ? parseInt(localStorage.getItem('font_size')) : 16;
  155.  
  156. panel.appendChild(document.createTextNode('Font: '));
  157. panel.appendChild(fontSelector);
  158. panel.appendChild(document.createElement('br'));
  159. panel.appendChild(document.createTextNode('Font Size: '));
  160. panel.appendChild(fontSizeInput);
  161. panel.appendChild(document.createElement('br'));
  162.  
  163. const buttonContainer = document.createElement('div');
  164. buttonContainer.className = 'mt-6 flex justify-between';
  165.  
  166. const okButton = document.createElement('button');
  167. okButton.textContent = 'Confirm';
  168. okButton.className = 'w-24 h-10 rounded bg-green-500 hover:bg-green-600 text-white';
  169. okButton.addEventListener('click', function () {
  170. categories.forEach(category => {
  171. const oldValue = localStorage.getItem(`${category}_color`);
  172. const newValue = colorPickers[category].value;
  173.  
  174. if (oldValue !== newValue) {
  175. localStorage.setItem(`${category}_color`, newValue);
  176. if (category === 'plaintext') {
  177. window.location.reload();
  178. }
  179. }
  180. });
  181.  
  182. localStorage.setItem('font_family', fontSelector.value);
  183. localStorage.setItem('font_size', fontSizeInput.value + 'px');
  184. window.location.reload();
  185. });
  186.  
  187. const cancelButton = document.createElement('button');
  188. cancelButton.textContent = 'Cancel';
  189. cancelButton.className = 'w-24 h-10 rounded bg-red-500 hover:bg-red-600 text-white';
  190. cancelButton.addEventListener('click', function () {
  191. panel.remove();
  192. });
  193.  
  194. buttonContainer.appendChild(okButton);
  195. buttonContainer.appendChild(cancelButton);
  196. panel.appendChild(buttonContainer);
  197.  
  198. document.body.appendChild(panel);
  199. }
  200.  
  201. function getDefaultColor(category) {
  202. const defaultColors = {
  203. 'italic': '#E0DF7F',
  204. 'quotationmarks': '#FFFFFF',
  205. 'plaintext': '#958C7F',
  206. 'custom': '#E0DF7F'
  207. };
  208. return defaultColors[category];
  209. }
  210.  
  211. const mainButton = createButton('', function () {
  212. const colorPanelExists = document.getElementById('colorPanel');
  213. if (!colorPanelExists) {
  214. createColorPanel();
  215. }
  216. });
  217.  
  218. mainButton.style.backgroundImage = "url('https://i.imgur.com/yBgJ3za.png')";
  219. mainButton.style.backgroundSize = "cover";
  220. mainButton.style.top = "0px";
  221. mainButton.style.width = "22px";
  222. mainButton.style.height = "22px";
  223.  
  224. function insertMainButton() {
  225. const targetSelector = '.chat2 > div:nth-child(1) > div:nth-child(1) > div:nth-child(3) > div:nth-child(1)';
  226. const targetPanel1 = document.querySelector(targetSelector);
  227.  
  228. if (targetPanel1) {
  229. if (!document.querySelector('.color-palette-button')) {
  230. mainButton.classList.add('color-palette-button');
  231. targetPanel1.insertBefore(mainButton, targetPanel1.firstChild);
  232. }
  233. } else {
  234. const observer = new MutationObserver(() => {
  235. const updatedTargetPanel = document.querySelector(targetSelector);
  236. if (updatedTargetPanel) {
  237. mainButton.classList.add('color-palette-button');
  238. updatedTargetPanel.insertBefore(mainButton, updatedTargetPanel.firstChild);
  239. observer.disconnect();
  240. }
  241. });
  242.  
  243. observer.observe(document.body, { subtree: true, childList: true });
  244. console.error('Target panel not found. Waiting for changes...');
  245. }
  246. }
  247.  
  248. setInterval(insertMainButton, 1000);
  249. insertMainButton();