你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式
(我已經安裝了使用者樣式管理器,讓我安裝!)
// ==UserScript==
// @name Sync between Sexy.AI and SillyTavern old
// @namespace http://tampermonkey.net/
// @version 2.4
// @description Enhanced integration between SillyTavern and Sexy.AI with translation support
// @author You
// @match https://sexy.ai/workflow*
// @match https://staticui.sexy.ai/*
// @match http://ducninh.top:8000/*
// @match http://127.0.0.1:8000/*
// @match http://*/*:8000/*
// @grant GM_setValue
// @grant GM_getValue
// @grant unsafeWindow
// ==/UserScript==
(function() {
'use strict';
console.log("Script started on URL:", window.location.href);
const isSexyAI = window.location.href.includes('sexy.ai') || window.location.href.includes('staticui.sexy.ai');
const isSillyTavern = window.location.href.includes(':8000');
// Utility Functions
function createStyledButton(text, onClick, isFixed = false) {
const button = document.createElement('button');
button.textContent = text;
let baseStyle = `
padding: 5px 8px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 12px;
opacity: 0.8;
transition: all 0.3s;
z-index: 9999;
`;
if(isFixed) {
baseStyle += `
position: fixed;
right: 20px;
`;
} else {
baseStyle += `
margin-left: 5px;
`;
}
button.style.cssText = baseStyle;
button.addEventListener('mouseover', () => button.style.opacity = '1');
button.addEventListener('mouseout', () => button.style.opacity = '0.8');
button.addEventListener('click', onClick);
return button;
}
// Extract original text from message
function extractOriginalText(messageNode) {
// Try to get original text from data attribute first
const originalText = messageNode.getAttribute('data-original-text');
if (originalText) {
return originalText;
}
// If no stored original text, get current text
const messageText = messageNode.querySelector('.mes_text');
return messageText ? messageText.textContent : '';
}
// Store original text before translation
function storeOriginalText(messageNode) {
const messageText = messageNode.querySelector('.mes_text');
if (messageText && !messageNode.hasAttribute('data-original-text')) {
messageNode.setAttribute('data-original-text', messageText.textContent);
}
}
// SexyAI Implementation
if (isSexyAI) {
if (window.location.href.includes('staticui.sexy.ai')) {
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);
promptButton.style.backgroundColor = '#2196F3';
promptButton.textContent = 'Prompt Added';
setTimeout(() => {
promptButton.style.backgroundColor = '#4CAF50';
promptButton.textContent = 'Get Prompt';
}, 2000);
}
}
}, true);
promptButton.style.top = '80px';
document.body.appendChild(promptButton);
}
document.addEventListener('click', (e) => {
if (e.target.tagName === 'IMG') {
const markdownUrls = [``];
console.log('Saving image URL:', markdownUrls[0]);
GM_setValue('sexyai_images', markdownUrls.join('\n'));
// Visual feedback on image click
const overlay = document.createElement('div');
overlay.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
background-color: #4CAF50;
color: white;
padding: 8px;
border-radius: 4px;
z-index: 10000;
opacity: 0;
transition: opacity 0.3s;
`;
overlay.textContent = 'Image Copied';
document.body.appendChild(overlay);
setTimeout(() => {
overlay.style.opacity = '1';
setTimeout(() => {
overlay.style.opacity = '0';
setTimeout(() => overlay.remove(), 300);
}, 1500);
}, 0);
}
}, true);
} else if (isSillyTavern) {
let isMonitoring = false;
function addMonitorButton() {
const extensionsButton = document.querySelector('#extensionsMenuButton');
if (extensionsButton && !document.querySelector('#monitor_button')) {
const monitorButton = document.createElement('div');
monitorButton.id = 'monitor_button';
monitorButton.className = 'fa-solid fa-eye menu_button';
monitorButton.title = 'Monitor Messages';
monitorButton.style.cssText = `
display: flex;
cursor: pointer;
opacity: 0.7;
margin: 0 5px;
font-size: 18px;
transition: all 0.3s;
`;
monitorButton.addEventListener('click', () => {
isMonitoring = !isMonitoring;
monitorButton.style.color = isMonitoring ? '#4CAF50' : '';
monitorButton.style.opacity = isMonitoring ? '1' : '0.7';
if (isMonitoring) {
processExistingMessages();
} else {
document.querySelectorAll('.button-container').forEach(container => {
container.remove();
});
}
});
extensionsButton.parentNode.insertBefore(monitorButton, extensionsButton.nextSibling);
}
}
function editMessageAndAddImage(messageNode, markdownUrls) {
return new Promise((resolve, reject) => {
try {
console.log('Starting edit process...');
const editButton = messageNode.querySelector('.mes_edit');
if (!editButton) {
throw new Error('Edit button not found');
}
editButton.click();
setTimeout(() => {
const textarea = document.getElementById('curEditTextarea');
if (!textarea) {
reject(new Error('Textarea not found'));
return;
}
// Get original text if available
const originalText = extractOriginalText(messageNode);
textarea.value = originalText + '\n' + markdownUrls;
textarea.dispatchEvent(new Event('input', { bubbles: true }));
setTimeout(() => {
const confirmButton = messageNode.querySelector('.mes_edit_done');
if (!confirmButton) {
reject(new Error('Confirm button not found'));
return;
}
confirmButton.click();
resolve();
}, 500);
}, 500);
} catch (error) {
reject(error);
}
});
}
function addButtonsToMessage(messageNode) {
if (!isMonitoring) return;
if (!messageNode || !messageNode.querySelector) return;
// Store original text before any translation happens
storeOriginalText(messageNode);
const messageText = messageNode.querySelector('.mes_text');
if (!messageText) return;
if (messageText.querySelector('.button-container')) return;
// Get original text for processing
const text = extractOriginalText(messageNode);
const hasImagePrompt = text.match(/image###([^#]+)###/);
if (hasImagePrompt) {
const buttonContainer = document.createElement('div');
buttonContainer.className = 'button-container';
buttonContainer.style.display = 'flex';
buttonContainer.style.gap = '5px';
buttonContainer.style.marginTop = '5px';
const syncButton = createStyledButton('📥 Sync Image', async () => {
console.log('Sync button clicked');
try {
const markdownUrls = GM_getValue('sexyai_images', null);
if (!markdownUrls) return;
await editMessageAndAddImage(messageNode, markdownUrls);
GM_setValue('sexyai_images', null);
syncButton.style.backgroundColor = '#2196F3';
syncButton.textContent = '✓ Synced';
setTimeout(() => {
syncButton.style.backgroundColor = '#4CAF50';
syncButton.textContent = '📥 Sync Image';
}, 2000);
} catch (error) {
console.error('Error in sync process:', error);
syncButton.style.backgroundColor = '#f44336';
syncButton.textContent = '❌ Error';
setTimeout(() => {
syncButton.style.backgroundColor = '#4CAF50';
syncButton.textContent = '📥 Sync Image';
}, 2000);
}
});
const sendPromptButton = createStyledButton('📤 Send Prompt', () => {
const text = extractOriginalText(messageNode);
const match = text.match(/image###([^#]+)###/);
if (match) {
const prompt = match[1].trim();
GM_setValue('st_prompt', prompt);
// Update visual text without affecting original stored text
const textNodes = Array.from(messageText.childNodes).filter(node => node.nodeType === Node.TEXT_NODE);
textNodes.forEach(node => {
node.textContent = node.textContent.replace(/(image###[^#]+)###/g, '');
});
sendPromptButton.style.backgroundColor = '#2196F3';
sendPromptButton.textContent = '✓ Sent';
setTimeout(() => {
sendPromptButton.style.backgroundColor = '#4CAF50';
sendPromptButton.textContent = '📤 Send Prompt';
}, 2000);
}
});
buttonContainer.appendChild(syncButton);
buttonContainer.appendChild(sendPromptButton);
messageText.appendChild(buttonContainer);
}
}
function processExistingMessages() {
const messages = document.getElementsByClassName('mes');
Array.from(messages).forEach(addButtonsToMessage);
}
setInterval(addMonitorButton, 2000);
const observer = new MutationObserver((mutations) => {
if (!isMonitoring) return;
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1) {
if (node.classList?.contains('mes')) {
addButtonsToMessage(node);
}
const mesElements = node.getElementsByClassName('mes');
Array.from(mesElements).forEach(addButtonsToMessage);
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
})();