Greasy Fork is available in English.
Sync between Sexy.AI and SillyTavern with improved mobile support
当前为
// ==UserScript==
// @name Sexy.AI to SillyTavern origin
// @namespace http://tampermonkey.net/
// @version 1.11
// @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';
// Add CSS for mobile and desktop
GM_addStyle(`
.ai-button {
display: inline-block !important;
min-height: 44px !important;
padding: 12px 20px !important;
background-color: #4CAF50 !important;
color: white !important;
border: none !important;
border-radius: 5px !important;
cursor: pointer !important;
font-size: 16px !important;
margin: 10px 5px !important;
opacity: 0.8;
transition: opacity 0.3s;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
}
.ai-button:active {
opacity: 1;
}
.button-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 10px;
width: 100%;
}
@media (max-width: 768px) {
.ai-button {
padding: 15px 25px !important;
font-size: 18px !important;
min-width: 120px;
}
.button-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 10px;
background: rgba(0,0,0,0.8);
z-index: 9999;
justify-content: center;
}
.fixed-button {
position: fixed !important;
z-index: 9999 !important;
bottom: 20px !important;
right: 20px !important;
}
}
`);
function isMobileDevice() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
function createStyledButton(text, onClick, isFixed = false) {
const button = document.createElement('button');
button.textContent = text;
button.className = 'ai-button';
if(isFixed && isMobileDevice()) {
button.classList.add('fixed-button');
}
// Add touch events for mobile
button.addEventListener('touchstart', (e) => {
e.preventDefault();
button.style.opacity = '1';
});
button.addEventListener('touchend', (e) => {
e.preventDefault();
button.style.opacity = '0.8';
onClick();
});
// Keep click event for desktop
button.addEventListener('click', onClick);
return button;
}
const isSexyAI = window.location.href.includes('staticui.sexy.ai');
const isSillyTavern = window.location.href.includes('ducninh.top:8000');
if (isSexyAI) {
const promptButton = createStyledButton('Get Prompt', () => {
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 {
console.error('Available inputs:', document.querySelectorAll('input, textarea'));
alert('Input field not found.');
}
} else {
alert('No prompt found. Copy from SillyTavern first.');
}
}, true);
document.body.appendChild(promptButton);
document.addEventListener('click', (e) => {
if (e.target.tagName === 'IMG') {
const markdownUrls = [``];
GM_setValue('sexyai_images', markdownUrls.join('\n'));
alert('Image copied! Switch to SillyTavern tab.');
}
}, true);
} else if (isSillyTavern) {
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('.button-container')) {
const buttonContainer = document.createElement('div');
buttonContainer.className = 'button-container';
const syncButton = createStyledButton('📥 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 = createStyledButton('📤 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###');
}
});
buttonContainer.appendChild(syncButton);
buttonContainer.appendChild(sendPromptButton);
messageText.appendChild(buttonContainer);
}
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
})();