Xoul AI Background Manager

Customize the chat interface on xoul.ai

安装此脚本
作者推荐脚本

您可能也喜欢Xoul AI Chat Unified Styler

安装此脚本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Xoul AI Background Manager
// @author      LuxTallis
// @namespace   xoul.ai Background Manager
// @match       https://xoul.ai/*
// @grant       none
// @license     MIT
// @version     1.0
// @description Customize the chat interface on xoul.ai
// @icon        https://i.imgur.com/REqi6Iw.png
// ==/UserScript==

(function () {
  'use strict';

    let currentChatId = null; // To track the active chat ID

function observeChatNavigation(callback) {
  let lastPathname = window.location.pathname;

  // Check for URL changes periodically
  setInterval(() => {
    if (window.location.pathname !== lastPathname) {
      lastPathname = window.location.pathname;
      callback(); // Trigger the callback on URL change
    }
  }, 500); // Check every 500ms
}


  // Function to get the current chat ID from the URL
  function getChatId() {
    const match = window.location.pathname.match(/\/chats\/([a-f0-9\-]+)/);
    return match ? match[1] : null;
  }

// Store a background for the current chat
function saveBackgroundImageForChat(chatId, url) {
  if (!chatId) return;
  let data = localStorage.getItem('background_images');
  data = data ? JSON.parse(data) : {};
  data[chatId] = url; // Save the background for this chat
  localStorage.setItem('background_images', JSON.stringify(data));
}


// Retrieve the background for the current chat
function getBackgroundImageForChat(chatId) {
  if (!chatId) return null;
  let data = localStorage.getItem('background_images');
  data = data ? JSON.parse(data) : {};
  return data[chatId] || null; // Return the saved background, or null if none
}

    // Reapply the background for the current chat on page load
function reapplyBackgroundForCurrentChat() {
  const chatId = getChatId();
  if (!chatId) return; // Exit if chatId is not valid
  const savedBackground = getBackgroundImageForChat(chatId);
  if (savedBackground) {
    applyBackgroundImage(savedBackground);
  }
}

    function handleDynamicChatSwitch() {
  const newChatId = getChatId();
  if (newChatId && newChatId !== currentChatId) {
    currentChatId = newChatId; // Update the tracked chat ID
    const savedBackground = getBackgroundImageForChat(newChatId);

    if (savedBackground) {
      applyBackgroundImage(savedBackground); // Apply the new background
    } else {
      removeBackgroundImage(); // Reset to default if no background is set
    }
  }
}


  // Get all background images for all chats from localStorage
  function getAllBackgroundImages() {
    return JSON.parse(localStorage.getItem('background_images') || '{}');
  }

  // Save the image library to localStorage
  function saveLibrary(library) {
    localStorage.setItem('background_image_library', JSON.stringify(library));
  }

  // Get the image library from localStorage
  function getLibrary() {
    return JSON.parse(localStorage.getItem('background_image_library') || '[]');
  }

  // Add an image URL to the library
  function addToLibrary(url) {
    const library = getLibrary();
    if (!url || library.includes(url)) return;
    library.push(url);
    saveLibrary(library);
  }

  // Remove an image URL from the library
  function removeFromLibrary(url) {
    const library = getLibrary().filter((image) => image !== url);
    saveLibrary(library);
  }

  // Apply background image for the specific chat
  function applyBackgroundImage(url) {
    const css = `
      .ChatUI_container_chatui__m5apj {
        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;
  }

  // Remove the background for the current chat (revert to default)
  function removeBackgroundImage() {
    const styleElement = document.getElementById('customBackgroundStyle');
    if (styleElement) {
      styleElement.remove();
    }
  }

  function createCustomizationPanel() {
    const chatId = getChatId(); // Get the current chat ID
    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 = '#0a0a0a';
    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';
    panel.style.minWidth = '375px';
    panel.style.width = 'auto';

    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 removeBackgroundButton = document.createElement('button');
    removeBackgroundButton.textContent = 'Remove Background';
    removeBackgroundButton.style.marginTop = '10px';
    removeBackgroundButton.style.padding = '5px 10px';
    removeBackgroundButton.style.border = 'none';
    removeBackgroundButton.style.borderRadius = '3px';
    removeBackgroundButton.style.backgroundColor = '#d9534f';
    removeBackgroundButton.style.color = 'white';
    removeBackgroundButton.style.fontFamily = 'Montserrat, sans-serif';
    removeBackgroundButton.addEventListener('click', () => {
      removeBackgroundImage();
      saveBackgroundImageForChat(chatId, null); // Remove background for current chat
    });

    const libraryContainer = document.createElement('div');
    libraryContainer.id = 'libraryContainer';
    libraryContainer.style.marginTop = '10px';
    libraryContainer.style.overflowX = 'auto';
    libraryContainer.style.display = 'flex';
    libraryContainer.style.flexWrap = 'wrap';
    libraryContainer.style.gap = '15px';
    libraryContainer.style.paddingBottom = '10px';
    libraryContainer.style.borderTop = '1px solid #555';
    libraryContainer.style.paddingTop = '10px';
    libraryContainer.style.whiteSpace = 'nowrap';
    libraryContainer.style.maxHeight = '380px';
    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';

        const img = document.createElement('img');
        img.src = url;
        img.alt = 'Preview';
        img.style.width = '99px';
        img.style.height = '64px';
        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);
          saveBackgroundImageForChat(chatId, url); // Save background for current chat
        });

        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(removeBackgroundButton);
    panel.appendChild(libraryContainer);
    document.body.appendChild(panel);

    renderLibrary();

    // Handle panel close when clicking outside
    const closePanel = (event) => {
      const button1 = document.getElementById('customButton'); // Ensure we reference the button here
      if (!panel.contains(event.target) && !button1.contains(event.target)) {
        panel.remove();
        document.removeEventListener('click', closePanel);
      }
    };

    document.addEventListener('click', closePanel);
  }

function ensureButtonUnderTarget() {
  const sidebarNav = document.querySelector('.Sidebar_nav__a5780');
  const existingButton = sidebarNav ? sidebarNav.querySelector('button:nth-child(7)') : null; // Check if the 7th button exists

  if (existingButton) {
    // If the 7th button exists, insert the custom button below it
    if (!document.querySelector('#customButton')) {
      const button = document.createElement('button');
      button.id = 'customButton';
      button.textContent = '♥';
      button.style.cssText = 'margin: 10px 0; padding: 5px 10px; background-color: #007BFF00; color: white; border: none; border-radius: 5px; cursor: pointer; display: block;';

      button.addEventListener('click', (e) => {
        e.stopPropagation(); // Prevent the click event from closing the panel immediately
        createCustomizationPanel();
      });

      existingButton.insertAdjacentElement('afterend', button); // Insert the button below the 7th button
    }
  } else {
    // If the 7th button doesn't exist, add the new button below the target link
    const targetElement = document.querySelector('a.Sidebar_link__0EvG_:nth-child(6)');
    if (targetElement && !document.querySelector('#customButton')) {
      const button = document.createElement('button');
      button.id = 'customButton';
      button.textContent = '♥';
      button.style.cssText = 'margin: 10px 0; padding: 5px 10px; background-color: #007BFF00; color: white; border: none; border-radius: 5px; cursor: pointer; display: block;';

      button.addEventListener('click', (e) => {
        e.stopPropagation(); // Prevent the click event from closing the panel immediately
        createCustomizationPanel();
      });

      targetElement.insertAdjacentElement('afterend', button); // Insert the button below the target element
    }
  }
}


  const observer = new MutationObserver(() => {
    ensureButtonUnderTarget();
  });

  observer.observe(document.body, { childList: true, subtree: true });

  ensureButtonUnderTarget();

    // Observe navigation and dynamically update the background
observeChatNavigation(handleDynamicChatSwitch);


  // Call this after page load
reapplyBackgroundForCurrentChat();

})();

QingJ © 2025

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