Jimpl SD Prompt Parser & Copier

Separates and formats Stable Diffusion prompts from EXIF data on jimpl.com

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         Jimpl SD Prompt Parser & Copier
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Separates and formats Stable Diffusion prompts from EXIF data on jimpl.com
// @author       qualia
// @match        *://jimpl.com/results/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=jimpl.com
// @license      MIT
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    function createSectionBox(title, content, showCopy = true) {
        if (!content) return null;

        const container = document.createElement('div');
        container.style.marginBottom = '12px';
        container.style.padding = '12px';
        container.style.backgroundColor = 'rgba(255, 255, 255, 0.05)';
        container.style.border = '1px solid rgba(255, 255, 255, 0.1)';
        container.style.borderRadius = '6px';
        container.style.position = 'relative';

        const header = document.createElement('strong');
        header.innerText = title;
        header.style.display = 'block';
        header.style.marginBottom = '8px';
        header.style.color = '#e0e0e0';
        header.style.fontSize = '14px';

        const textNode = document.createElement('div');
        textNode.innerText = content;
        textNode.style.wordBreak = 'break-word';
        textNode.style.color = '#aaaaaa';
        textNode.style.fontSize = '13px';
        textNode.style.lineHeight = '1.4';
        textNode.style.maxHeight = '200px';
        textNode.style.overflowY = 'auto';

        container.appendChild(header);

        if (showCopy) {
            const copyBtn = document.createElement('button');
            copyBtn.innerText = 'Copy';
            copyBtn.style.position = 'absolute';
            copyBtn.style.top = '8px';
            copyBtn.style.right = '8px';
            copyBtn.style.padding = '4px 10px';
            copyBtn.style.cursor = 'pointer';
            copyBtn.style.backgroundColor = '#3b82f6';
            copyBtn.style.color = 'white';
            copyBtn.style.border = 'none';
            copyBtn.style.borderRadius = '4px';
            copyBtn.style.fontSize = '12px';
            copyBtn.style.fontWeight = 'bold';
            copyBtn.style.transition = 'background-color 0.2s';

            copyBtn.onmouseover = () => copyBtn.style.backgroundColor = '#2563eb';
            copyBtn.onmouseout = () => copyBtn.style.backgroundColor = '#3b82f6';

            copyBtn.onclick = () => {
                navigator.clipboard.writeText(content).then(() => {
                    const originalText = copyBtn.innerText;
                    copyBtn.innerText = 'Copied!';
                    copyBtn.style.backgroundColor = '#10b981';
                    setTimeout(() => {
                        copyBtn.innerText = originalText;
                        copyBtn.style.backgroundColor = '#3b82f6';
                    }, 1500);
                });
            };
            container.appendChild(copyBtn);
        }

        container.appendChild(textNode);

        return container;
    }

    function parseAndInject() {
        const rows = Array.from(document.querySelectorAll('tr'));
        const targetRow = rows.find(tr => {
            const firstCell = tr.children[0];
            if (!firstCell) return false;
            const text = firstCell.textContent.trim();
            return text === 'UserComment' || text === 'parameters';
        });

        if (!targetRow || targetRow.dataset.parsed === 'true') return;

        const valueCell = targetRow.children[1];
        if (!valueCell) return;

        const fullText = valueCell.textContent.trim();

        if (!fullText.includes('Steps:')) return;

        targetRow.dataset.parsed = 'true';

        let positive = "";
        let negative = "";
        let settings = "";

        const negativeIndex = fullText.indexOf("Negative prompt:");
        const stepsIndex = fullText.lastIndexOf("Steps:");

        if (negativeIndex !== -1 && stepsIndex !== -1 && negativeIndex < stepsIndex) {
            positive = fullText.substring(0, negativeIndex).trim();
            negative = fullText.substring(negativeIndex + 16, stepsIndex).trim();
            settings = fullText.substring(stepsIndex).trim();
        } else if (negativeIndex !== -1) {
            positive = fullText.substring(0, negativeIndex).trim();
            negative = fullText.substring(negativeIndex + 16).trim();
        } else if (stepsIndex !== -1) {
            positive = fullText.substring(0, stepsIndex).trim();
            settings = fullText.substring(stepsIndex).trim();
        } else {
            positive = fullText;
        }

        valueCell.innerHTML = '';
        valueCell.style.padding = '10px';

        const posBox = createSectionBox('Positive Prompt', positive, true);
        const negBox = createSectionBox('Negative Prompt', negative, true);
        const setBox = createSectionBox('Settings', settings, false);

        if (posBox) valueCell.appendChild(posBox);
        if (negBox) valueCell.appendChild(negBox);
        if (setBox) valueCell.appendChild(setBox);
    }

    const observer = new MutationObserver((mutations) => {
        parseAndInject();
    });

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

    window.addEventListener('load', parseAndInject);
    parseAndInject();

})();