您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Customize the chat interface with an improved image library for backgrounds, with multiple rows of thumbnails and click-outside popup closure.
- // ==UserScript==
- // @name c.ai Background Image Library (Improved)
- // @author LuxTallis
- // @namespace c.ai Background Image Library Improved
- // @match https://character.ai/*
- // @grant none
- // @license MIT
- // @version 1.1
- // @description Customize the chat interface with an improved image library for backgrounds, with multiple rows of thumbnails and click-outside popup closure.
- // @icon https://i.imgur.com/ynjBqKW.png
- // ==/UserScript==
- (function () {
- function saveLibrary(library) {
- localStorage.setItem('background_image_library', JSON.stringify(library));
- }
- function getLibrary() {
- return JSON.parse(localStorage.getItem('background_image_library') || '[]');
- }
- function addToLibrary(url) {
- const library = getLibrary();
- if (!url || library.includes(url)) return;
- library.push(url);
- saveLibrary(library);
- }
- function removeFromLibrary(url) {
- const library = getLibrary().filter((image) => image !== url);
- saveLibrary(library);
- }
- // Function to get the current chat ID
- function getChatID() {
- const path = window.location.pathname;
- const match = path.match(/\/chat\/([^/]+)/); // Matches chat ID in URL
- return match ? match[1] : null; // Return the chat ID if found
- }
- // Apply background image for the specific chat
- function applyBackgroundImage(url) {
- const chatID = getChatID();
- if (!chatID) return; // If no chat ID found, return
- const chatKey = `background_image_${chatID}`;
- localStorage.setItem(chatKey, url); // Save the background URL for this chat
- const css = `
- body {
- background-image: url('${url}');
- background-size: cover;
- background-position: center;
- background-repeat: no-repeat;
- }
- `;
- let styleElement = document.getElementById('customBackgroundStyle');
- if (!styleElement) {
- styleElement = document.createElement('style');
- styleElement.id = 'customBackgroundStyle';
- document.head.appendChild(styleElement);
- }
- styleElement.innerHTML = css;
- }
- function createCustomizationPanel() {
- const panel = document.createElement('div');
- panel.id = 'customizationPanel';
- panel.style.position = 'fixed';
- panel.style.top = '50%';
- panel.style.left = '50%';
- panel.style.transform = 'translate(-50%, -50%)';
- panel.style.backgroundColor = '#1e1e1e';
- panel.style.color = 'white';
- panel.style.borderRadius = '5px';
- panel.style.padding = '20px';
- panel.style.zIndex = '9999';
- panel.style.fontFamily = 'Montserrat, sans-serif';
- panel.style.maxWidth = '1250px'; // 2.5x wider
- panel.style.minWidth = '875px'; // 2.5x wider
- panel.style.width = 'auto'; // Ensure auto width based on content
- const label = document.createElement('label');
- label.textContent = 'Add Image URL to Library:';
- label.style.display = 'block';
- label.style.marginBottom = '5px';
- const input = document.createElement('input');
- input.type = 'text';
- input.placeholder = 'Enter image URL';
- input.style.width = '100%';
- input.style.marginBottom = '10px';
- const addButton = document.createElement('button');
- addButton.textContent = 'Add';
- addButton.style.marginTop = '10px';
- addButton.style.padding = '5px 10px';
- addButton.style.border = 'none';
- addButton.style.borderRadius = '3px';
- addButton.style.backgroundColor = '#444';
- addButton.style.color = 'white';
- addButton.style.fontFamily = 'Montserrat, sans-serif';
- addButton.addEventListener('click', () => {
- const url = input.value.trim();
- if (url) {
- addToLibrary(url);
- input.value = '';
- renderLibrary();
- }
- });
- const libraryContainer = document.createElement('div');
- libraryContainer.id = 'libraryContainer';
- libraryContainer.style.marginTop = '10px';
- libraryContainer.style.overflowX = 'auto';
- libraryContainer.style.display = 'flex';
- libraryContainer.style.flexWrap = 'wrap'; // Allow wrapping into multiple lines
- libraryContainer.style.gap = '15px'; // Increased gap between thumbnails
- libraryContainer.style.paddingBottom = '10px';
- libraryContainer.style.borderTop = '1px solid #555';
- libraryContainer.style.paddingTop = '10px';
- libraryContainer.style.whiteSpace = 'nowrap'; // Prevent wrapping of thumbnails
- libraryContainer.style.maxHeight = '380px'; // Ensure 3 rows of thumbnails
- libraryContainer.style.height = 'auto';
- function renderLibrary() {
- libraryContainer.innerHTML = '';
- const library = getLibrary();
- library.forEach((url) => {
- const imgContainer = document.createElement('div');
- imgContainer.style.position = 'relative';
- imgContainer.style.flex = '0 0 auto'; // Ensures the thumbnail stays at its natural width
- const img = document.createElement('img');
- img.src = url;
- img.alt = 'Preview';
- img.style.width = '99px'; // Half the size
- img.style.height = '64px'; // Half the size
- img.style.objectFit = 'cover';
- img.style.border = '1px solid #fff';
- img.style.borderRadius = '3px';
- img.style.cursor = 'pointer';
- img.title = url;
- img.addEventListener('click', () => {
- applyBackgroundImage(url);
- });
- const removeButton = document.createElement('button');
- removeButton.textContent = '×';
- removeButton.style.position = 'absolute';
- removeButton.style.top = '5px';
- removeButton.style.right = '5px';
- removeButton.style.backgroundColor = 'red';
- removeButton.style.color = 'white';
- removeButton.style.border = 'none';
- removeButton.style.borderRadius = '50%';
- removeButton.style.cursor = 'pointer';
- removeButton.style.width = '20px';
- removeButton.style.height = '20px';
- removeButton.style.textAlign = 'center';
- removeButton.style.fontSize = '12px';
- removeButton.addEventListener('click', (e) => {
- e.stopPropagation();
- removeFromLibrary(url);
- renderLibrary();
- });
- imgContainer.appendChild(img);
- imgContainer.appendChild(removeButton);
- libraryContainer.appendChild(imgContainer);
- });
- }
- panel.appendChild(label);
- panel.appendChild(input);
- panel.appendChild(addButton);
- panel.appendChild(libraryContainer);
- document.body.appendChild(panel);
- renderLibrary();
- // Close the panel when clicking outside of it
- document.addEventListener('click', function closeOnOutsideClick(event) {
- if (!panel.contains(event.target) && !mainButton.contains(event.target)) {
- panel.remove();
- document.removeEventListener('click', closeOnOutsideClick); // Remove the event listener
- }
- });
- }
- function createButton(symbol, onClick) {
- const button = document.createElement('button');
- button.innerHTML = symbol;
- button.style.position = 'fixed';
- button.style.top = '82px';
- button.style.right = '5px';
- button.style.width = '22px';
- button.style.height = '22px';
- button.style.backgroundColor = '#444';
- button.style.color = 'white';
- button.style.border = 'none';
- button.style.borderRadius = '3px';
- button.style.cursor = 'pointer';
- button.style.fontFamily = 'Montserrat, sans-serif';
- button.addEventListener('click', onClick);
- return button;
- }
- const mainButton = createButton('🖼️', () => {
- const panelExists = document.getElementById('customizationPanel');
- if (!panelExists) {
- createCustomizationPanel();
- }
- });
- document.body.appendChild(mainButton);
- // Function to update background when URL changes
- function updateBackgroundForCurrentChat() {
- const chatID = getChatID();
- const currentImageUrl = chatID ? localStorage.getItem(`background_image_${chatID}`) : '';
- applyBackgroundImage(currentImageUrl || '');
- }
- // Initial background update
- updateBackgroundForCurrentChat();
- // Update background whenever the URL changes
- window.addEventListener('popstate', updateBackgroundForCurrentChat);
- window.addEventListener('pushstate', updateBackgroundForCurrentChat);
- window.addEventListener('replacestate', updateBackgroundForCurrentChat);
- })();