Greasy Fork

Greasy Fork is available in English.

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.

当前为 2024-11-25 提交的版本,查看 最新版本

// ==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     6.3
// @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 applyBackgroundImage(url) {
    localStorage.setItem('background_image_url', url);
    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);

  const currentImageUrl = localStorage.getItem('background_image_url') || '';
  applyBackgroundImage(currentImageUrl);
})();