c.ai X Text Color

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

目前为 2024-03-17 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name c.ai X Text Color
  3. // @namespace c.ai X Text Color
  4. // @match https://character.ai/*
  5. // @grant none
  6. // @license MIT
  7. // @version 2.6.5
  8. // @author Vishanka via 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.  
  14. (function () {
  15. var plaintextColor = localStorage.getItem('plaintext_color');
  16. var italicColor = localStorage.getItem('italic_color');
  17. var charbubbleColor = localStorage.getItem('charbubble_color') || '#26272B';
  18. var userbubbleColor = localStorage.getItem('userbubble_color') || '#303136';
  19. // Default color if 'plaintext_color' is not set
  20. var defaultColor = '#A2A2AC';
  21.  
  22. // Use the retrieved color or default color
  23. var color = plaintextColor || defaultColor;
  24.  
  25. // Create the CSS style
  26. var css = "p[node='[object Object]'] { color: " + color + " !important; font-family: '__Inter_918210','Noto Sans', sans-serif !important; } p, textarea, button, div.text-sm { font-family: '__Inter_918210','Noto Sans', sans-serif !important; } em { color: " + italicColor + " !important; }";
  27.  
  28. // Add specific CSS based on window location
  29. /* css += window.location.href.includes("character.ai/chat") ?
  30. `.bg-surface-elevation-2 { background-color: ${charbubbleColor}; } .bg-surface-elevation-3 { background-color: ${userbubbleColor}; }` :
  31. `.bg-surface-elevation-2, .bg-surface-elevation-3 { background-color: #26272B; }`;
  32. */
  33. css += `.mt-1.bg-surface-elevation-2 { background-color: ${charbubbleColor}; } .mt-1.bg-surface-elevation-3 { background-color: ${userbubbleColor}; }`;
  34.  
  35.  
  36.  
  37. var head = document.getElementsByTagName("head")[0];
  38. var style = document.createElement("style");
  39. style.setAttribute("type", "text/css");
  40. style.innerHTML = css;
  41. head.appendChild(style);
  42. })();
  43.  
  44.  
  45.  
  46. function changeColors() {
  47. const pTags = document.getElementsByTagName("p");
  48. const quotationMarksColor = localStorage.getItem('quotationmarks_color') || '#FFFFFF';
  49. const customColor = localStorage.getItem('custom_color') || '#FFFFFF';
  50. const wordlistCc = JSON.parse(localStorage.getItem('wordlist_cc')) || [];
  51.  
  52. const wordRegex = wordlistCc.length > 0 ? new RegExp('\\b(' + wordlistCc.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|') + ')\\b', 'gi') : null;
  53.  
  54. const changes = [];
  55.  
  56. for (let i = 0; i < pTags.length; i++) {
  57. const pTag = pTags[i];
  58. if (
  59. pTag.dataset.colorChanged === "true" ||
  60. pTag.querySelector("code") ||
  61. pTag.querySelector("img") ||
  62. pTag.querySelector("textarea") ||
  63. pTag.querySelector("button") ||
  64. pTag.querySelector("div")
  65. ) {
  66. continue;
  67. }
  68. let text = pTag.innerHTML;
  69.  
  70. //Changes Text within Quotation Marks to white
  71. text = text.replace(/(["“”«»].*?["“”«»])/g, `<span style="color: ${quotationMarksColor}">$1</span>`);
  72. //Changes Text within Quotation Marks and a comma at the end to yellow
  73. text = text.replace(/(["“”«»][^"]*?,["“”«»])/g, `<span style="color: #E0DF7F">$1</span>`);
  74.  
  75. if (wordRegex) {
  76. text = text.replace(wordRegex, `<span style="color: ${customColor}">$1</span>`);
  77. }
  78.  
  79. changes.push({ pTag, text });
  80. }
  81.  
  82. changes.forEach(change => {
  83. const { pTag, text } = change;
  84. pTag.innerHTML = text;
  85. pTag.dataset.colorChanged = "true";
  86. });
  87.  
  88. console.log("Changed colors");
  89. }
  90.  
  91.  
  92.  
  93. const divElements = document.querySelectorAll('div');
  94.  
  95. divElements.forEach(p => {
  96. const observer = new MutationObserver(changeColors);
  97. observer.observe(p, { subtree: true, childList: true });
  98. });
  99.  
  100.  
  101.  
  102.  
  103.  
  104. function createButton(symbol, onClick) {
  105. const colorpalettebutton = document.createElement('button');
  106. colorpalettebutton.innerHTML = symbol;
  107. colorpalettebutton.style.position = 'relative';
  108. colorpalettebutton.style.background = 'none';
  109. colorpalettebutton.style.border = 'none';
  110. colorpalettebutton.style.fontSize = '18px';
  111. colorpalettebutton.style.top = '-5px';
  112. colorpalettebutton.style.cursor = 'pointer';
  113. colorpalettebutton.addEventListener('click', onClick);
  114. return colorpalettebutton;
  115. }
  116.  
  117. // Function to create the color selector panel
  118. function createColorPanel() {
  119. const panel = document.createElement('div');
  120. panel.id = 'colorPanel';
  121. panel.style.position = 'fixed';
  122. panel.style.top = '50%';
  123. panel.style.left = '50%';
  124. panel.style.transform = 'translate(-50%, -50%)';
  125. panel.style.backgroundColor = 'rgba(19, 19, 22, 0.95)';
  126. panel.style.border = 'none';
  127. panel.style.borderRadius = '5px';
  128. panel.style.padding = '20px';
  129. // panel.style.border = '2px solid #000';
  130. panel.style.zIndex = '9999';
  131.  
  132. const categories = ['italic', 'quotationmarks', 'plaintext', 'custom', 'charbubble', 'userbubble'];
  133.  
  134. const colorPickers = {};
  135.  
  136. // Set a fixed width for the labels
  137. const labelWidth = '150px';
  138.  
  139. categories.forEach(category => {
  140. const colorPicker = document.createElement('input');
  141. colorPicker.type = 'color';
  142.  
  143. // Retrieve stored color from local storage
  144. const storedColor = localStorage.getItem(`${category}_color`);
  145. if (storedColor) {
  146. colorPicker.value = storedColor;
  147. }
  148.  
  149. colorPickers[category] = colorPicker;
  150.  
  151. // Create a div to hold color picker
  152. const colorDiv = document.createElement('div');
  153. colorDiv.style.position = 'relative';
  154. colorDiv.style.width = '20px';
  155. colorDiv.style.height = '20px';
  156. colorDiv.style.marginLeft = '10px';
  157. colorDiv.style.top = '5px';
  158. colorDiv.style.backgroundColor = colorPicker.value;
  159. colorDiv.style.display = 'inline-block';
  160. colorDiv.style.marginRight = '10px';
  161. colorDiv.style.cursor = 'pointer';
  162. colorDiv.style.border = '1px solid black';
  163.  
  164.  
  165. // Event listener to open color picker when the color square is clicked
  166. colorDiv.addEventListener('click', function () {
  167. colorPicker.click();
  168. });
  169.  
  170. // Event listener to update the color div when the color changes
  171. colorPicker.addEventListener('input', function () {
  172. colorDiv.style.backgroundColor = colorPicker.value;
  173. });
  174.  
  175. const label = document.createElement('label');
  176. label.style.width = labelWidth; // Set fixed width for the label
  177. label.appendChild(document.createTextNode(`${category}: `));
  178.  
  179. // Reset button for each color picker
  180. const resetButton = createButton('↺', function () {
  181. colorPicker.value = getDefaultColor(category);
  182. colorDiv.style.backgroundColor = colorPicker.value;
  183. });
  184. resetButton.style.position = 'relative';
  185. resetButton.style.top = '1px';
  186. // Create a div to hold label, color picker, and reset button
  187. const containerDiv = document.createElement('div');
  188. containerDiv.appendChild(label);
  189. containerDiv.appendChild(colorDiv);
  190. containerDiv.appendChild(resetButton);
  191.  
  192. panel.appendChild(containerDiv);
  193. panel.appendChild(document.createElement('br'));
  194. });
  195.  
  196. // Custom word list input
  197. const wordListInput = document.createElement('input');
  198. wordListInput.type = 'text';
  199. wordListInput.placeholder = 'Separate words with commas';
  200. wordListInput.style.width = '250px';
  201. wordListInput.style.height = '35px';
  202. wordListInput.style.borderRadius = '3px';
  203. wordListInput.style.marginBottom = '10px';
  204. panel.appendChild(wordListInput);
  205. panel.appendChild(document.createElement('br'));
  206.  
  207. const wordListContainer = document.createElement('div');
  208. wordListContainer.style.display = 'flex';
  209. wordListContainer.style.flexWrap = 'wrap';
  210. wordListContainer.style.maxWidth = '300px'; // Set a fixed maximum width for the container
  211.  
  212. // Display custom word list buttons
  213. const wordListArray = JSON.parse(localStorage.getItem('wordlist_cc')) || [];
  214. const wordListButtons = [];
  215.  
  216. function createWordButton(word) {
  217. const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
  218.  
  219. const removeSymbol = isMobile ? '×' : '🞮';
  220.  
  221. const wordButton = createButton(`${word} ${removeSymbol}`, function() {
  222. // Remove the word from the list and update the panel
  223. const index = wordListArray.indexOf(word);
  224. if (index !== -1) {
  225. wordListArray.splice(index, 1);
  226. updateWordListButtons();
  227. }
  228. });
  229.  
  230. // Word Buttons
  231. wordButton.style.borderRadius = '3px';
  232. wordButton.style.border = 'none';
  233. wordButton.style.backgroundColor = '#26272B';
  234. wordButton.style.marginBottom = '5px';
  235. wordButton.style.marginRight = '5px';
  236. wordButton.style.fontSize = '16px';
  237. wordButton.classList.add('word-button');
  238. return wordButton;
  239. }
  240.  
  241. function updateWordListButtons() {
  242. wordListContainer.innerHTML = ''; // Clear the container
  243. wordListArray.forEach(word => {
  244. const wordButton = createWordButton(word);
  245. wordListContainer.appendChild(wordButton);
  246. });
  247. }
  248.  
  249. // Append wordListContainer to the panel
  250.  
  251.  
  252.  
  253. updateWordListButtons();
  254.  
  255. // Add Words button
  256. const addWordsButton = document.createElement('button');
  257. addWordsButton.textContent = 'Add';
  258. addWordsButton.style.marginTop = '-8px';
  259. addWordsButton.style.marginLeft = '5px';
  260. addWordsButton.style.borderRadius = '3px';
  261. addWordsButton.style.border = 'none';
  262. addWordsButton.style.backgroundColor = '#26272B';
  263. addWordsButton.addEventListener('click', function() {
  264. // Get the input value, split into words, and add to wordListArray
  265. const wordListValue = wordListInput.value;
  266. const newWords = wordListValue.split(',').map(word => word.trim().toLowerCase()).filter(word => word !== ''); // Convert to lowercase and remove empty entries
  267. wordListArray.push(...newWords);
  268.  
  269. // Update the word list buttons in the panel
  270. updateWordListButtons();
  271. });
  272.  
  273. // Create a div to group the input and button on the same line
  274. const inputButtonContainer = document.createElement('div');
  275. inputButtonContainer.style.display = 'flex';
  276. inputButtonContainer.style.alignItems = 'center';
  277.  
  278. inputButtonContainer.appendChild(wordListInput);
  279. inputButtonContainer.appendChild(addWordsButton);
  280.  
  281. // Append the container to the panel
  282. panel.appendChild(inputButtonContainer);
  283. panel.appendChild(wordListContainer);
  284. // Create initial word list buttons
  285. updateWordListButtons();
  286.  
  287.  
  288. // OK button
  289. const okButton = document.createElement('button');
  290. okButton.textContent = 'Confirm';
  291. okButton.style.marginTop = '-20px';
  292. okButton.style.width = '75px';
  293. okButton.style.height = '35px';
  294. okButton.style.marginRight = '5px';
  295. okButton.style.borderRadius = '3px';
  296. okButton.style.border = 'none';
  297. okButton.style.backgroundColor = '#26272B';
  298. okButton.style.position = 'relative';
  299. okButton.style.left = '24%';
  300. //okButton.style.transform = 'translateX(-50%)';
  301. okButton.addEventListener('click', function() {
  302. // Save selected colors to local storage
  303. categories.forEach(category => {
  304. const oldValue = localStorage.getItem(`${category}_color`);
  305. const newValue = colorPickers[category].value;
  306.  
  307. if (oldValue !== newValue) {
  308. localStorage.setItem(`${category}_color`, newValue);
  309.  
  310. // If 'plaintext' color is changed, auto-reload the page
  311. if (category === 'plaintext') {
  312. window.location.reload();
  313. }
  314. }
  315. });
  316.  
  317.  
  318. // Save custom word list to local storage
  319. const wordListValue = wordListInput.value;
  320. const newWords = wordListValue.split(',').map(word => word.trim().toLowerCase()).filter(word => word !== ''); // Convert to lowercase and remove empty entries
  321. const uniqueNewWords = Array.from(new Set(newWords)); // Remove duplicates
  322.  
  323. // Check for existing words and add only new ones
  324. uniqueNewWords.forEach(newWord => {
  325. if (!wordListArray.includes(newWord)) {
  326. wordListArray.push(newWord);
  327. }
  328. });
  329.  
  330. localStorage.setItem('wordlist_cc', JSON.stringify(wordListArray));
  331.  
  332. updateWordListButtons();
  333.  
  334. // Close the panel
  335. panel.remove();
  336. });
  337.  
  338. // Cancel button
  339. const cancelButton = document.createElement('button');
  340. cancelButton.textContent = 'Cancel';
  341. cancelButton.style.marginTop = '-20px';
  342. cancelButton.style.borderRadius = '3px';
  343. cancelButton.style.width = '75px';
  344. cancelButton.style.marginLeft = '5px';
  345. cancelButton.style.height = '35px';
  346. cancelButton.style.border = 'none';
  347. cancelButton.style.backgroundColor = '#5E5E5E';
  348. cancelButton.style.position = 'relative';
  349. cancelButton.style.left = '25%';
  350. cancelButton.addEventListener('click', function() {
  351. // Close the panel without saving
  352. panel.remove();
  353. });
  354.  
  355. panel.appendChild(document.createElement('br'));
  356. panel.appendChild(okButton);
  357. panel.appendChild(cancelButton);
  358.  
  359. document.body.appendChild(panel);
  360. }
  361.  
  362.  
  363.  
  364. // Function to get the default color for a category
  365. function getDefaultColor(category) {
  366. const defaultColors = {
  367. 'italic': '#E0DF7F',
  368. 'quotationmarks': '#FFFFFF',
  369. 'plaintext': '#A2A2AC',
  370. 'custom': '#E0DF7F',
  371. 'charbubble': '#26272B',
  372. 'userbubble': '#303136'
  373. };
  374. return defaultColors[category];
  375. }
  376.  
  377. const mainButton = createButton('', function() {
  378. const colorPanelExists = document.getElementById('colorPanel');
  379. if (!colorPanelExists) {
  380. createColorPanel();
  381. }
  382. });
  383.  
  384. // Set the background image of the button to the provided image
  385. mainButton.style.backgroundImage = "url('https://i.imgur.com/yBgJ3za.png')";
  386. mainButton.style.backgroundSize = "cover";
  387. mainButton.style.position = "fixed"; // Use "fixed" for a position relative to the viewport
  388. mainButton.style.top = "10px"; // Adjust the top position as needed
  389. mainButton.style.right = "10px"; // Adjust the right position as needed
  390. mainButton.style.width = "22px"; // Adjust the width and height as needed
  391. mainButton.style.height = "22px"; // Adjust the width and height as needed
  392.  
  393. // Function to insert the mainButton into the body of the document
  394. function insertMainButton() {
  395. document.body.appendChild(mainButton);
  396. }
  397.  
  398. // Call the function to insert the mainButton into the body
  399. insertMainButton();
  400.  
  401. console.error('Main button appended to the top right corner.');