Greasy Fork

Greasy Fork is available in English.

Sync between Sexy.AI and SillyTavern old

Enhanced integration between SillyTavern and Sexy.AI with translation support

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

// ==UserScript==
// @name         Sync between Sexy.AI and SillyTavern old
// @namespace    http://tampermonkey.net/
// @version      3.0
// @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) {
        const button = document.createElement('button');
        button.textContent = text;
        button.style.cssText = `
            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;
            margin-left: 5px;
        `;
        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');
        if (!messageText) return '';

        // Clone the message text element to work with
        const clone = messageText.cloneNode(true);

        // Remove any button containers if they exist
        const buttonContainer = clone.querySelector('.button-container');
        if (buttonContainer) {
            buttonContainer.remove();
        }

        return clone.textContent.trim();
    }

    // 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);
                    }
                }
            });
            promptButton.style.cssText += 'position: fixed; right: 20px; top: 80px;';
            document.body.appendChild(promptButton);
        }

document.addEventListener('click', (e) => {
        if (e.target.tagName === 'IMG') {
            // Lấy tất cả ảnh hiện tại
            const allImages = document.querySelectorAll('img');
            const latestImages = Array.from(allImages)
                .slice(-4) // Lấy 4 ảnh cuối cùng
                .map(img => `![alt-text](${img.src})`);

            console.log('Saving latest images:', latestImages);
            GM_setValue('sexyai_images', latestImages.join('\n'));

            // Visual feedback
            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 = 'Images Synced';
            document.body.appendChild(overlay);

            setTimeout(() => {
                overlay.style.opacity = '1';
                setTimeout(() => {
                    overlay.style.opacity = '0';
                    setTimeout(() => overlay.remove(), 300);
                }, 1500);
            }, 0);
        }
    }, true);
}

     else if (isSillyTavern) {
        function showImageModal(imageUrl) {
    const existingModal = document.querySelector('.image-modal-container');
    if (existingModal) {
        existingModal.remove();
    }

    const container = document.createElement('div');
    container.className = 'image-modal-container';
    container.style.cssText = `
        position: fixed;
        top: 20px;
        right: 20px;
        z-index: 10000;
        background: rgba(0, 0, 0, 0.8);
        padding: 10px;
        border-radius: 10px;
        max-width: 300px;
    `;

    // Tải trước tất cả các ảnh
    const images = GM_getValue('sexyai_images', '').split('\n').filter(url => url);
    const imageElements = images.map(url => {
        const img = new Image();
        img.src = url.replace('![alt-text](', '').replace(')', '');
        img.style.cssText = `
            width: 100%;
            height: auto;
            max-height: 400px;
            object-fit: contain;
            border-radius: 5px;
            display: none;
        `;
        return img;
    });

    let currentIndex = images.findIndex(url => url === imageUrl);
    if (currentIndex === -1) currentIndex = 0;

    // Hiển thị ảnh hiện tại
    imageElements[currentIndex].style.display = 'block';

    // Thêm tất cả ảnh vào container
    imageElements.forEach(img => container.appendChild(img));

    const navigationContainer = document.createElement('div');
    navigationContainer.style.cssText = `
        display: flex;
        justify-content: space-between;
        margin-top: 10px;
    `;

    const prevButton = document.createElement('button');
    const nextButton = document.createElement('button');
    [prevButton, nextButton].forEach(button => {
        button.style.cssText = `
            background: #4CAF50;
            border: none;
            color: white;
            padding: 5px 15px;
            border-radius: 3px;
            cursor: pointer;
            margin: 0 5px;
            font-size: 16px;
            transition: background-color 0.2s;
        `;
    });
    prevButton.textContent = '←';
    nextButton.textContent = '→';

    // Tối ưu hàm chuyển ảnh
    function switchImage(newIndex) {
        imageElements[currentIndex].style.display = 'none';
        currentIndex = (newIndex + images.length) % images.length;
        imageElements[currentIndex].style.display = 'block';
    }

    prevButton.onclick = () => switchImage(currentIndex - 1);
    nextButton.onclick = () => switchImage(currentIndex + 1);

    // Thêm phím tắt để điều hướng
    document.addEventListener('keydown', function(e) {
        if (container.isConnected) {  // Chỉ xử lý khi modal đang hiển thị
            if (e.key === 'ArrowLeft') {
                prevButton.click();
            } else if (e.key === 'ArrowRight') {
                nextButton.click();
            } else if (e.key === 'Escape') {
                closeButton.click();
            }
        }
    });

    // Cải thiện nút đóng
    const closeButton = document.createElement('button');
    closeButton.textContent = '×';
    closeButton.style.cssText = `
        position: absolute;
        top: -10px;
        right: -10px;
        background: #4CAF50;
        border: none;
        color: white;
        width: 20px;
        height: 20px;
        border-radius: 50%;
        font-size: 14px;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 0;
        transition: background-color 0.2s;
    `;

    closeButton.onmouseover = () => closeButton.style.backgroundColor = '#45a049';
    closeButton.onmouseout = () => closeButton.style.backgroundColor = '#4CAF50';

    closeButton.onclick = () => {
        container.remove();
        const showImageButton = document.querySelector('#show_image_button');
        if (showImageButton) {
            showImageButton.style.color = '';
            showImageButton.style.opacity = '0.7';
        }
    };

    container.appendChild(closeButton);
    navigationContainer.appendChild(prevButton);
    navigationContainer.appendChild(nextButton);
    container.appendChild(navigationContainer);
    document.body.appendChild(container);

    // Tải trước ảnh kế tiếp
    images.forEach((url, index) => {
        if (index !== currentIndex) {
            const preloadImg = new Image();
            preloadImg.src = url.replace('![alt-text](', '').replace(')', '');
        }
    });
}

        function addControlButtons() {
            // Kiểm tra cả hai nút để tránh nhân bản
            if (document.querySelector('#show_image_button') || document.querySelector('#send_prompt_button')) {
                return; // Nếu đã tồn tại nút thì không thêm nữa
            }

            const extensionsButton = document.querySelector('#extensionsMenuButton');
            const optionsButton = document.querySelector('#options_button');

            if (extensionsButton && optionsButton) {
                // Nút Show Image
                const showImageButton = document.createElement('div');
                showImageButton.id = 'show_image_button';
                showImageButton.className = 'fa-solid fa-eye interactable';
                showImageButton.title = 'Show/Hide Images';
                showImageButton.style.cssText = `
                    display: flex;
                    cursor: pointer;
                    opacity: 0.7;
                    margin: 0 5px;
                    font-size: 18px;
                    transition: all 0.3s;
                `;
                showImageButton.tabIndex = "0";

                // Nút Send Prompt
                const sendPromptButton = document.createElement('div');
                sendPromptButton.id = 'send_prompt_button';
                sendPromptButton.className = 'fa-solid fa-paper-plane interactable';
                sendPromptButton.title = 'Send Prompt';
                sendPromptButton.style.cssText = `
                    display: flex;
                    cursor: pointer;
                    opacity: 0.7;
                    margin: 0 5px;
                    font-size: 18px;
                    transition: all 0.3s;
                `;
                sendPromptButton.tabIndex = "0";

                // Logic xử lý sự kiện cho Show Image Button
                let isShowingImages = false;
                let currentImageIndex = 0;
                let images = [];

                showImageButton.addEventListener('click', () => {
                    isShowingImages = !isShowingImages;
                    showImageButton.style.color = isShowingImages ? 'var(--accent-color, #4CAF50)' : '';
                    showImageButton.style.opacity = isShowingImages ? '1' : '0.7';

                    if (isShowingImages) {
                        const markdownUrls = GM_getValue('sexyai_images', '').split('\n').filter(url => url);
                        images = markdownUrls;
                        if (images.length > 0) {
                            showImageModal(images[currentImageIndex]);
                        }
                    } else {
                        const existingModal = document.querySelector('.image-modal-container');
                        if (existingModal) {
                            existingModal.remove();
                        }
                    }
                });

                // Logic xử lý sự kiện cho Send Prompt Button
                sendPromptButton.addEventListener('click', () => {
                    const messages = document.getElementsByClassName('mes');
                    const lastMessage = messages[messages.length - 1];
                    if (lastMessage) {
                        const text = extractOriginalText(lastMessage);
                        const match = text.match(/image###([^#]+)###/);
                        if (match) {
                            const prompt = match[1].trim();
                            GM_setValue('st_prompt', prompt);
                            sendPromptButton.style.color = 'var(--accent-color, #4CAF50)';
                            setTimeout(() => {
                                sendPromptButton.style.color = '';
                            }, 2000);
                        }
                    }
                });

                // Chèn các nút vào vị trí phù hợp
                optionsButton.parentNode.insertBefore(showImageButton, optionsButton.nextSibling);
                extensionsButton.parentNode.insertBefore(sendPromptButton, extensionsButton.nextSibling);
            }
        }

        // Thay đổi cách gọi hàm addControlButtons
        let checkInterval = setInterval(() => {
            if (document.querySelector('#options_button') && document.querySelector('#extensionsMenuButton')) {
                addControlButtons();
                // Sau khi thêm nút thành công, clear interval
                if (document.querySelector('#show_image_button') && document.querySelector('#send_prompt_button')) {
                    clearInterval(checkInterval);
                }
            }
        }, 1000);

        // Thêm một observer để xử lý trường hợp giao diện bị reload
        const observer = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                if (mutation.removedNodes.length > 0) {
                    // Nếu các nút bị xóa, thêm lại chúng
                    if (!document.querySelector('#show_image_button') || !document.querySelector('#send_prompt_button')) {
                        addControlButtons();
                    }
                }
            }
        });

        // Bắt đầu quan sát DOM
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }
})();