Greasy Fork

Greasy Fork is available in English.

Sexy.AI to SillyTavern origin

Sync between Sexy.AI and SillyTavern with improved mobile support

目前为 2024-11-20 提交的版本。查看 最新版本

// ==UserScript==
// @name         Sexy.AI to SillyTavern origin
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Sync between Sexy.AI and SillyTavern with improved mobile support
// @author       You
// @match        https://sexy.ai/workflow*
// @match        https://staticui.sexy.ai/*
// @match        http://ducninh.top:8000/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        unsafeWindow
// @grant        GM_addStyle
// ==/UserScript==
(function() {
   'use strict';

   GM_addStyle(`
       #sexyai-panel input, #sexyai-panel select {
           background-color: #444;
           color: white;
           background-color: black;
           border: none;
           padding: 5px;
           margin: 5px 0;
           white-space: nowrap;
       }
       #sexyai-panel button {
           background-color: #444;
           color: white;
           border: none;
           padding: 5px 10px;
           cursor: pointer;
       }
       #sexyai-panel button:hover {
           background-color: #555;
       }
   `);

   const isSexyAI = window.location.href.includes('staticui.sexy.ai');
   const isSillyTavern = window.location.href.includes('ducninh.top:8000');
   let panel;

   function addSexyAIButton() {
       const targetElement = document.querySelector('#option_toggle_AN');
       if (targetElement) {
           const newElement = document.createElement('a');
           newElement.id = 'option_toggle_sexyai';

           const icon = document.createElement('i');
           icon.className = 'fa-lg fa-solid fa-images';
           newElement.appendChild(icon);

           const span = document.createElement('span');
           span.setAttribute('data-i18n', "SexyAI Tools");
           span.textContent = 'SexyAI Tools';
           newElement.appendChild(span);

           targetElement.parentNode.insertBefore(newElement, targetElement.nextSibling);
           newElement.addEventListener('click', showSexyAIPanel);
       }
   }

   function createSexyAIPanel() {
       const panel = document.createElement('div');
       panel.id = 'sexyai-panel';
       panel.style.cssText = `
           position: absolute;
           top: 50%;
           left: 50%;
           transform: translate(-50%, -50%);
           background-color: black;
           color: white;
           padding: 20px;
           border: 1px solid white;
           z-index: 10000;
           display: none;
           overflow-y: auto;
           max-height: 80vh;
       `;

       let html = `
           <h2>SexyAI Tools</h2>
       `;

       if (isSexyAI) {
           html += `
               <button id="get-prompt">Get Prompt</button><br>
               <button id="close-panel">Close</button>
           `;
       }

       if (isSillyTavern) {
           html += `
               <button id="sync-image">📥 Sync Image</button><br>
               <button id="send-prompt">📤 Send Prompt</button><br>
               <button id="close-panel">Close</button>
           `;
       }

       panel.innerHTML = html;
       document.body.appendChild(panel);

       panel.querySelector('#close-panel').addEventListener('click', hideSexyAIPanel);

       if (isSexyAI) {
           panel.querySelector('#get-prompt').addEventListener('click', () => {
               const prompt = GM_getValue('st_prompt', null);
               if (prompt) {
                   const positiveInput = document.querySelector('textarea') ||
                                       document.querySelector('input[type="text"]');
                   if (positiveInput) {
                       positiveInput.value = prompt;
                       const event = new Event('input', { bubbles: true });
                       positiveInput.dispatchEvent(event);
                       GM_setValue('st_prompt', null);
                       alert('Prompt added!');
                   }
               } else {
                   alert('No prompt found. Copy from SillyTavern first.');
               }
               hideSexyAIPanel();
           });
       }

       return panel;
   }

   function showSexyAIPanel() {
       if (!panel) {
           panel = createSexyAIPanel();
       }
       panel.style.display = 'block';
   }

   function hideSexyAIPanel() {
       if (panel) {
           panel.style.display = 'none';
       }
   }

   if (isSexyAI) {
       const checkInterval = setInterval(() => {
           if (document.querySelector('#option_toggle_AN')) {
               clearInterval(checkInterval);
               addSexyAIButton();
           }
       }, 1000);

       document.addEventListener('click', (e) => {
           if (e.target.tagName === 'IMG') {
               const markdownUrls = [`![alt-text](${e.target.src})`];
               GM_setValue('sexyai_images', markdownUrls.join('\n'));
               alert('Image copied! Switch to SillyTavern tab.');
           }
       }, true);
   }

   if (isSillyTavern) {
       const checkInterval = setInterval(() => {
           if (document.querySelector('#option_toggle_AN')) {
               clearInterval(checkInterval);
               addSexyAIButton();
           }
       }, 1000);

       const observer = new MutationObserver((mutations) => {
           mutations.forEach((mutation) => {
               mutation.addedNodes.forEach((node) => {
                   if (node.classList?.contains('mes')) {
                       const messageText = node.querySelector('.mes_text');
                       if (messageText && !messageText.querySelector('.sexyai-tools')) {
                           const syncButton = createButton('📥 Sync Image', () => {
                               const markdownUrls = GM_getValue('sexyai_images', null);
                               if (!markdownUrls) {
                                   alert('No images found. Click an image in Sexy.AI first.');
                                   return;
                               }

                               const editButton = node.querySelector('.mes_edit');
                               if (editButton) {
                                   editButton.click();
                                   setTimeout(() => {
                                       const textarea = document.getElementById('curEditTextarea');
                                       if (textarea) {
                                           textarea.value = textarea.value + '\n' + markdownUrls;
                                           setTimeout(() => {
                                               const confirmButton = node.querySelector('.mes_edit_done');
                                               if (confirmButton) {
                                                   confirmButton.click();
                                                   GM_setValue('sexyai_images', null);
                                                   alert('Images added successfully!');
                                               }
                                           }, 100);
                                       }
                                   }, 100);
                               }
                           });

                           const sendPromptButton = createButton('📤 Send Prompt', () => {
                               const text = messageText.textContent;
                               const match = text.match(/image###([^#]+)###/);
                               if (match) {
                                   const prompt = match[1].trim();
                                   GM_setValue('st_prompt', prompt);
                                   alert('Prompt copied! Click "Get Prompt" in Sexy.AI tab');
                               } else {
                                   alert('No valid prompt found. Message should contain image###prompt###');
                               }
                           });

                           const container = document.createElement('div');
                           container.className = 'sexyai-tools';
                           container.appendChild(syncButton);
                           container.appendChild(sendPromptButton);
                           messageText.appendChild(container);
                       }
                   }
               });
           });
       });

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

   function createButton(text, onClick) {
       const button = document.createElement('button');
       button.className = 'sexyai-button';
       button.textContent = text;
       button.addEventListener('click', onClick);
       button.style.cssText = `
           margin: 5px;
           padding: 5px 10px;
           background-color: #4CAF50;
           color: white;
           border: none;
           border-radius: 5px;
           cursor: pointer;
       `;
       return button;
   }

})();