c.ai Enhanced Color Customizer

Enhanced color and UI customization for Character.AI

  1. // ==UserScript==
  2. // @name c.ai Enhanced Color Customizer
  3. // @namespace cai-color-customizer
  4. // @match https://character.ai/*
  5. // @grant GM_addStyle
  6. // @grant GM_setValue
  7. // @grant GM_getValue
  8. // @license MIT
  9. // @version 1.0
  10. // @description Enhanced color and UI customization for Character.AI
  11. // @icon https://i.imgur.com/ynjBqKW.png
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. // Helper function to get the current theme
  18. function getCurrentTheme() {
  19. return document.documentElement.classList.contains('dark') ? 'dark' : 'light';
  20. }
  21.  
  22. // Default colors based on theme
  23. function getDefaultColors(theme) {
  24. const darkColors = {
  25. 'italic': '#E0DF7F',
  26. 'quotationmarks': '#FFFFFF',
  27. 'plaintext': '#A2A2AC',
  28. 'custom': '#E0DF7F',
  29. 'charbubble': '#26272B',
  30. 'userbubble': '#303136',
  31. 'guide': '#131316',
  32. 'input': '#202024',
  33. 'body': '#18181B',
  34. 'accent': '#26272B'
  35. };
  36.  
  37. const lightColors = {
  38. 'italic': '#4F7AA6',
  39. 'quotationmarks': '#000000',
  40. 'plaintext': '#374151',
  41. 'custom': '#4F7AA6',
  42. 'charbubble': '#E4E4E7',
  43. 'userbubble': '#D9D9DF',
  44. 'guide': '#FAFAFA',
  45. 'input': '#F4F4F5',
  46. 'body': '#ECECEE',
  47. 'accent': '#26272B'
  48. };
  49.  
  50. return theme === 'dark' ? darkColors : lightColors;
  51. }
  52.  
  53. // Create main customization menu
  54. function createCustomizationMenu() {
  55. const theme = getCurrentTheme();
  56. const menuContainer = document.createElement('div');
  57. menuContainer.id = 'cai-color-customizer';
  58. menuContainer.style.cssText = `
  59. position: fixed;
  60. top: 50%;
  61. left: 50%;
  62. transform: translate(-50%, -50%);
  63. width: 900px;
  64. background-color: ${theme === 'dark' ? 'rgba(19, 19, 22, 0.95)' : 'rgba(214, 214, 221, 0.95)'};
  65. border-radius: 10px;
  66. padding: 20px;
  67. z-index: 9999;
  68. display: none;
  69. `;
  70.  
  71. // Tabs container
  72. const tabContainer = document.createElement('div');
  73. tabContainer.style.cssText = `
  74. display: flex;
  75. margin-bottom: 15px;
  76. `;
  77.  
  78. // Content container
  79. const contentContainer = document.createElement('div');
  80.  
  81. // Create tabs
  82. const tabs = [
  83. { name: 'Colors', id: 'colors' },
  84. { name: 'Typography', id: 'typography' },
  85. { name: 'Layout', id: 'layout' }
  86. ];
  87.  
  88. tabs.forEach(tab => {
  89. const tabButton = document.createElement('button');
  90. tabButton.textContent = tab.name;
  91. tabButton.style.cssText = `
  92. flex-grow: 1;
  93. padding: 10px;
  94. background-color: ${theme === 'dark' ? '#26272B' : '#E4E4E7'};
  95. border: none;
  96. margin-right: 5px;
  97. cursor: pointer;
  98. `;
  99.  
  100. tabButton.addEventListener('click', () => {
  101. // Hide all content
  102. contentContainer.innerHTML = '';
  103.  
  104. // Show specific tab content
  105. switch(tab.id) {
  106. case 'colors':
  107. renderColorTab(contentContainer);
  108. break;
  109. case 'typography':
  110. renderTypographyTab(contentContainer);
  111. break;
  112. case 'layout':
  113. renderLayoutTab(contentContainer);
  114. break;
  115. }
  116. });
  117.  
  118. tabContainer.appendChild(tabButton);
  119. });
  120.  
  121. // Add close button
  122. const closeButton = document.createElement('button');
  123. closeButton.textContent = '×';
  124. closeButton.style.cssText = `
  125. position: absolute;
  126. top: 10px;
  127. right: 10px;
  128. background: none;
  129. border: none;
  130. font-size: 20px;
  131. cursor: pointer;
  132. `;
  133. closeButton.addEventListener('click', () => {
  134. menuContainer.style.display = 'none';
  135. });
  136.  
  137. menuContainer.appendChild(closeButton);
  138. menuContainer.appendChild(tabContainer);
  139. menuContainer.appendChild(contentContainer);
  140.  
  141. document.body.appendChild(menuContainer);
  142. return menuContainer;
  143. }
  144.  
  145. // Render color customization tab
  146. function renderColorTab(container) {
  147. const theme = getCurrentTheme();
  148. const categories = ['italic', 'quotationmarks', 'plaintext', 'custom', 'charbubble', 'userbubble', 'guide', 'input', 'body', 'accent'];
  149.  
  150. categories.forEach(category => {
  151. const colorWrapper = document.createElement('div');
  152. colorWrapper.style.cssText = `
  153. display: flex;
  154. align-items: center;
  155. margin-bottom: 10px;
  156. `;
  157.  
  158. const label = document.createElement('label');
  159. label.textContent = category.charAt(0).toUpperCase() + category.slice(1);
  160. label.style.marginRight = '10px';
  161.  
  162. const colorPicker = document.createElement('input');
  163. colorPicker.type = 'color';
  164. colorPicker.value = GM_getValue(`${category}_color`, getDefaultColors(theme)[category]);
  165.  
  166. colorPicker.addEventListener('input', () => {
  167. GM_setValue(`${category}_color`, colorPicker.value);
  168. applyCustomColors();
  169. });
  170.  
  171. colorWrapper.appendChild(label);
  172. colorWrapper.appendChild(colorPicker);
  173. container.appendChild(colorWrapper);
  174. });
  175. }
  176.  
  177. // Render typography tab
  178. function renderTypographyTab(container) {
  179. // Font selection, size, weight etc.
  180. const fonts = [
  181. { name: 'Inter', value: '__Inter_918210' },
  182. { name: 'Onest', value: '__Onest_b2ce1d' },
  183. { name: 'Noto Sans', value: 'Noto Sans' },
  184. { name: 'Arial', value: 'Arial' }
  185. ];
  186.  
  187. const fontSelect = document.createElement('select');
  188. fonts.forEach(font => {
  189. const option = document.createElement('option');
  190. option.value = font.value;
  191. option.text = font.name;
  192. fontSelect.appendChild(option);
  193. });
  194.  
  195. fontSelect.value = GM_getValue('selected_font', '__Inter_918210');
  196. fontSelect.addEventListener('change', () => {
  197. GM_setValue('selected_font', fontSelect.value);
  198. applyTypographySettings();
  199. });
  200.  
  201. container.appendChild(fontSelect);
  202. }
  203.  
  204. // Render layout tab
  205. function renderLayoutTab(container) {
  206. // Image size, margins, etc.
  207. const sizeInput = document.createElement('input');
  208. sizeInput.type = 'number';
  209. sizeInput.value = GM_getValue('image_size', '24');
  210. sizeInput.addEventListener('change', () => {
  211. GM_setValue('image_size', sizeInput.value);
  212. applyLayoutSettings();
  213. });
  214.  
  215. container.appendChild(sizeInput);
  216. }
  217.  
  218. // Apply color customizations dynamically
  219. function applyCustomColors() {
  220. const theme = getCurrentTheme();
  221. const defaultColors = getDefaultColors(theme);
  222. const categories = ['italic', 'quotationmarks', 'plaintext', 'custom', 'charbubble', 'userbubble', 'guide', 'input', 'body', 'accent'];
  223.  
  224. const styleElement = document.createElement('style');
  225. let css = '';
  226.  
  227. categories.forEach(category => {
  228. const color = GM_getValue(`${category}_color`, defaultColors[category]);
  229.  
  230. switch(category) {
  231. case 'italic':
  232. css += `em { color: ${color} !important; } `;
  233. break;
  234. case 'plaintext':
  235. css += `p[node='[object Object]'] { color: ${color} !important; } `;
  236. break;
  237. case 'charbubble':
  238. css += `.mt-1.bg-surface-elevation-2 { background-color: ${color}; } `;
  239. break;
  240. case 'userbubble':
  241. css += `.mt-1.bg-surface-elevation-3 { background-color: ${color}; } `;
  242. break;
  243. // Add more specific color mappings as needed
  244. }
  245. });
  246.  
  247. styleElement.textContent = css;
  248. document.head.appendChild(styleElement);
  249. }
  250.  
  251. // Apply typography settings
  252. function applyTypographySettings() {
  253. const font = GM_getValue('selected_font', '__Inter_918210');
  254. const styleElement = document.createElement('style');
  255. styleElement.textContent = `
  256. p, textarea, button, div.text-sm {
  257. font-family: '${font}', 'Noto Sans', sans-serif !important;
  258. }
  259. `;
  260. document.head.appendChild(styleElement);
  261. }
  262.  
  263. // Apply layout settings
  264. function applyLayoutSettings() {
  265. const imageSize = GM_getValue('image_size', '24') + 'px';
  266. const styleElement = document.createElement('style');
  267. styleElement.textContent = `
  268. .mt-0.hidden.md\\:flex.flex-col.gap-3.items-center img {
  269. width: ${imageSize} !important;
  270. height: ${imageSize} !important;
  271. }
  272. `;
  273. document.head.appendChild(styleElement);
  274. }
  275.  
  276. // Global customization button
  277. function createCustomizationButton() {
  278. const button = document.createElement('button');
  279. button.style.cssText = `
  280. position: fixed;
  281. top: 10px;
  282. right: 10px;
  283. width: 22px;
  284. height: 22px;
  285. background-image: url('https://i.imgur.com/yBgJ3za.png');
  286. background-size: cover;
  287. z-index: 9998;
  288. border: none;
  289. `;
  290.  
  291. const menu = createCustomizationMenu();
  292. button.addEventListener('click', () => {
  293. menu.style.display = menu.style.display === 'none' ? 'block' : 'none';
  294. });
  295.  
  296. document.body.appendChild(button);
  297. }
  298.  
  299. // Global keyboard shortcut handler
  300. function handleKeyboardShortcut(event) {
  301. if (event.key === '`' && event.ctrlKey) {
  302. event.preventDefault();
  303. const menu = document.getElementById('cai-color-customizer');
  304. if (menu) {
  305. menu.style.display = menu.style.display === 'none' ? 'block' : 'none';
  306. }
  307. }
  308. }
  309.  
  310. // Initialize customizations on page load
  311. function initialize() {
  312. createCustomizationButton();
  313. applyCustomColors();
  314. applyTypographySettings();
  315. applyLayoutSettings();
  316.  
  317. // Add keyboard shortcut listener
  318. document.addEventListener('keydown', handleKeyboardShortcut);
  319. }
  320.  
  321. // Run initialization
  322. initialize();
  323. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址