Greasy Fork 支持简体中文。

c.ai Background Image Library (Improved)

Customize the chat interface with an improved image library for backgrounds, with multiple rows of thumbnails and click-outside popup closure.

安裝腳本?
作者推薦腳本

您可能也會喜歡 CAI CharPic replacer

安裝腳本
  1. // ==UserScript==
  2. // @name c.ai Background Image Library (Improved)
  3. // @author LuxTallis
  4. // @namespace c.ai Background Image Library Improved
  5. // @match https://character.ai/*
  6. // @grant none
  7. // @license MIT
  8. // @version 1.1
  9. // @description Customize the chat interface with an improved image library for backgrounds, with multiple rows of thumbnails and click-outside popup closure.
  10. // @icon https://i.imgur.com/ynjBqKW.png
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. function saveLibrary(library) {
  15. localStorage.setItem('background_image_library', JSON.stringify(library));
  16. }
  17.  
  18. function getLibrary() {
  19. return JSON.parse(localStorage.getItem('background_image_library') || '[]');
  20. }
  21.  
  22. function addToLibrary(url) {
  23. const library = getLibrary();
  24. if (!url || library.includes(url)) return;
  25. library.push(url);
  26. saveLibrary(library);
  27. }
  28.  
  29. function removeFromLibrary(url) {
  30. const library = getLibrary().filter((image) => image !== url);
  31. saveLibrary(library);
  32. }
  33.  
  34. // Function to get the current chat ID
  35. function getChatID() {
  36. const path = window.location.pathname;
  37. const match = path.match(/\/chat\/([^/]+)/); // Matches chat ID in URL
  38. return match ? match[1] : null; // Return the chat ID if found
  39. }
  40.  
  41. // Apply background image for the specific chat
  42. function applyBackgroundImage(url) {
  43. const chatID = getChatID();
  44. if (!chatID) return; // If no chat ID found, return
  45.  
  46. const chatKey = `background_image_${chatID}`;
  47. localStorage.setItem(chatKey, url); // Save the background URL for this chat
  48.  
  49. const css = `
  50. body {
  51. background-image: url('${url}');
  52. background-size: cover;
  53. background-position: center;
  54. background-repeat: no-repeat;
  55. }
  56. `;
  57. let styleElement = document.getElementById('customBackgroundStyle');
  58. if (!styleElement) {
  59. styleElement = document.createElement('style');
  60. styleElement.id = 'customBackgroundStyle';
  61. document.head.appendChild(styleElement);
  62. }
  63. styleElement.innerHTML = css;
  64. }
  65.  
  66. function createCustomizationPanel() {
  67. const panel = document.createElement('div');
  68. panel.id = 'customizationPanel';
  69. panel.style.position = 'fixed';
  70. panel.style.top = '50%';
  71. panel.style.left = '50%';
  72. panel.style.transform = 'translate(-50%, -50%)';
  73. panel.style.backgroundColor = '#1e1e1e';
  74. panel.style.color = 'white';
  75. panel.style.borderRadius = '5px';
  76. panel.style.padding = '20px';
  77. panel.style.zIndex = '9999';
  78. panel.style.fontFamily = 'Montserrat, sans-serif';
  79. panel.style.maxWidth = '1250px'; // 2.5x wider
  80. panel.style.minWidth = '875px'; // 2.5x wider
  81. panel.style.width = 'auto'; // Ensure auto width based on content
  82.  
  83. const label = document.createElement('label');
  84. label.textContent = 'Add Image URL to Library:';
  85. label.style.display = 'block';
  86. label.style.marginBottom = '5px';
  87.  
  88. const input = document.createElement('input');
  89. input.type = 'text';
  90. input.placeholder = 'Enter image URL';
  91. input.style.width = '100%';
  92. input.style.marginBottom = '10px';
  93.  
  94. const addButton = document.createElement('button');
  95. addButton.textContent = 'Add';
  96. addButton.style.marginTop = '10px';
  97. addButton.style.padding = '5px 10px';
  98. addButton.style.border = 'none';
  99. addButton.style.borderRadius = '3px';
  100. addButton.style.backgroundColor = '#444';
  101. addButton.style.color = 'white';
  102. addButton.style.fontFamily = 'Montserrat, sans-serif';
  103. addButton.addEventListener('click', () => {
  104. const url = input.value.trim();
  105. if (url) {
  106. addToLibrary(url);
  107. input.value = '';
  108. renderLibrary();
  109. }
  110. });
  111.  
  112. const libraryContainer = document.createElement('div');
  113. libraryContainer.id = 'libraryContainer';
  114. libraryContainer.style.marginTop = '10px';
  115. libraryContainer.style.overflowX = 'auto';
  116. libraryContainer.style.display = 'flex';
  117. libraryContainer.style.flexWrap = 'wrap'; // Allow wrapping into multiple lines
  118. libraryContainer.style.gap = '15px'; // Increased gap between thumbnails
  119. libraryContainer.style.paddingBottom = '10px';
  120. libraryContainer.style.borderTop = '1px solid #555';
  121. libraryContainer.style.paddingTop = '10px';
  122. libraryContainer.style.whiteSpace = 'nowrap'; // Prevent wrapping of thumbnails
  123. libraryContainer.style.maxHeight = '380px'; // Ensure 3 rows of thumbnails
  124. libraryContainer.style.height = 'auto';
  125.  
  126. function renderLibrary() {
  127. libraryContainer.innerHTML = '';
  128. const library = getLibrary();
  129. library.forEach((url) => {
  130. const imgContainer = document.createElement('div');
  131. imgContainer.style.position = 'relative';
  132. imgContainer.style.flex = '0 0 auto'; // Ensures the thumbnail stays at its natural width
  133.  
  134. const img = document.createElement('img');
  135. img.src = url;
  136. img.alt = 'Preview';
  137. img.style.width = '99px'; // Half the size
  138. img.style.height = '64px'; // Half the size
  139. img.style.objectFit = 'cover';
  140. img.style.border = '1px solid #fff';
  141. img.style.borderRadius = '3px';
  142. img.style.cursor = 'pointer';
  143. img.title = url;
  144.  
  145. img.addEventListener('click', () => {
  146. applyBackgroundImage(url);
  147. });
  148.  
  149. const removeButton = document.createElement('button');
  150. removeButton.textContent = '×';
  151. removeButton.style.position = 'absolute';
  152. removeButton.style.top = '5px';
  153. removeButton.style.right = '5px';
  154. removeButton.style.backgroundColor = 'red';
  155. removeButton.style.color = 'white';
  156. removeButton.style.border = 'none';
  157. removeButton.style.borderRadius = '50%';
  158. removeButton.style.cursor = 'pointer';
  159. removeButton.style.width = '20px';
  160. removeButton.style.height = '20px';
  161. removeButton.style.textAlign = 'center';
  162. removeButton.style.fontSize = '12px';
  163. removeButton.addEventListener('click', (e) => {
  164. e.stopPropagation();
  165. removeFromLibrary(url);
  166. renderLibrary();
  167. });
  168.  
  169. imgContainer.appendChild(img);
  170. imgContainer.appendChild(removeButton);
  171. libraryContainer.appendChild(imgContainer);
  172. });
  173. }
  174.  
  175. panel.appendChild(label);
  176. panel.appendChild(input);
  177. panel.appendChild(addButton);
  178. panel.appendChild(libraryContainer);
  179. document.body.appendChild(panel);
  180.  
  181. renderLibrary();
  182.  
  183. // Close the panel when clicking outside of it
  184. document.addEventListener('click', function closeOnOutsideClick(event) {
  185. if (!panel.contains(event.target) && !mainButton.contains(event.target)) {
  186. panel.remove();
  187. document.removeEventListener('click', closeOnOutsideClick); // Remove the event listener
  188. }
  189. });
  190. }
  191.  
  192. function createButton(symbol, onClick) {
  193. const button = document.createElement('button');
  194. button.innerHTML = symbol;
  195. button.style.position = 'fixed';
  196. button.style.top = '82px';
  197. button.style.right = '5px';
  198. button.style.width = '22px';
  199. button.style.height = '22px';
  200. button.style.backgroundColor = '#444';
  201. button.style.color = 'white';
  202. button.style.border = 'none';
  203. button.style.borderRadius = '3px';
  204. button.style.cursor = 'pointer';
  205. button.style.fontFamily = 'Montserrat, sans-serif';
  206. button.addEventListener('click', onClick);
  207. return button;
  208. }
  209.  
  210. const mainButton = createButton('🖼️', () => {
  211. const panelExists = document.getElementById('customizationPanel');
  212. if (!panelExists) {
  213. createCustomizationPanel();
  214. }
  215. });
  216.  
  217. document.body.appendChild(mainButton);
  218.  
  219. // Function to update background when URL changes
  220. function updateBackgroundForCurrentChat() {
  221. const chatID = getChatID();
  222. const currentImageUrl = chatID ? localStorage.getItem(`background_image_${chatID}`) : '';
  223. applyBackgroundImage(currentImageUrl || '');
  224. }
  225.  
  226. // Initial background update
  227. updateBackgroundForCurrentChat();
  228.  
  229. // Update background whenever the URL changes
  230. window.addEventListener('popstate', updateBackgroundForCurrentChat);
  231. window.addEventListener('pushstate', updateBackgroundForCurrentChat);
  232. window.addEventListener('replacestate', updateBackgroundForCurrentChat);
  233. })();