Civitai Multiple Prompt Copy Buttons (with Prompt Management)

Adds multiple prompt copy buttons in a compact, draggable floating menu with prompt management on Civitai's generate page

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Civitai Multiple Prompt Copy Buttons (with Prompt Management)
// @namespace    http://tampermonkey.net/
// @version      4.0
// @description  Adds multiple prompt copy buttons in a compact, draggable floating menu with prompt management on Civitai's generate page
// @match        https://civitai.com/generate
// @grant        GM_setClipboard
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT

// ==/UserScript==

(function() {
    'use strict';

    let prompts = GM_getValue("savedPrompts", {
        'Ryuko': "matoi ryuuko, kill la kill, 1girl, black hair, blue eyes, medium hair, multicolored hair, red hair, streaked hair, two-tone hair",
        'Low angle': "from below, low angle, ground level",
        'High angle': "from above, high angle",
        'Kuroneko': "in the style of Kanzaki Hiro, 1girl, gokou ruri, black hair, blush, hairclip, hairband, hime cut, purple eyes, hair flower, hair ornament, hairclip, red eyes",
        'Toki': "toki (blue archive), blue archive, 1girl, blonde hair, blue eyes, blue hairband, blue halo, hairband, halo, solo",
        'Oily': "wet, wet skin, shiny, shiny skin, glossy, glossy skin, oil, oily, oily skin, sweat, sweaty, sweaty skin",
        'Dark': "dark-skinned female, dark skin, tan, tanline",
        'Blonde': "blonde hair, hime cut, long hair, straight hair, hairband"
    });

    const colors = ['#8B0000', '#00008B', '#8B008B', '#006400', '#008B8B', '#8B4500', '#8B8B00'];

    GM_addStyle(`
        #prompt-buttons-container {
            position: fixed;
            z-index: 9999;
            background-color: rgba(26,26,26,0.9);
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.3);
            font-family: Arial, sans-serif;
            width: 240px;
            display: flex;
            flex-direction: column;
        }
        #drag-handle {
            height: 24px;
            background-color: #2a2a2a;
            border-top-left-radius: 8px;
            border-top-right-radius: 8px;
            cursor: move;
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 0 8px;
        }
        #buttons-grid {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 4px;
            padding: 4px;
            max-height: 300px;
            overflow-y: auto;
        }
        .prompt-button, #show-hide-btn, #manage-btn {
            padding: 6px;
            color: white;
            border: none;
            cursor: pointer;
            font-size: 11px;
            font-weight: bold;
            transition: all 0.2s;
            text-align: center;
            border-radius: 4px;
        }
        .prompt-button:hover, #show-hide-btn:hover, #manage-btn:hover {
            filter: brightness(1.2);
            transform: translateY(-1px);
        }
        #show-hide-btn, #manage-btn {
            background-color: #4a4a4a;
            width: 50px;
        }
        #pin-btn {
            cursor: pointer;
            color: #fff;
            font-size: 14px;
            background: none;
            border: none;
            padding: 0;
        }
        #manage-panel {
            display: none;
            padding: 8px;
            background-color: #2a2a2a;
        }
        #manage-panel input, #manage-panel textarea {
            width: 100%;
            margin-bottom: 4px;
            padding: 4px;
        }
        #manage-panel button {
            margin-right: 4px;
            margin-bottom: 4px;
        }
    `);

    function createButton(name, prompt, color) {
        const button = document.createElement('button');
        button.className = 'prompt-button';
        button.textContent = name;
        button.style.backgroundColor = color;
        button.onclick = (e) => {
            e.stopPropagation();
            GM_setClipboard(prompt);
            const originalText = button.textContent;
            button.textContent = 'Copied!';
            setTimeout(() => button.textContent = originalText, 1000);
        };
        return button;
    }

    function makeDraggable(container, handle) {
        let isDragging = false;
        let startX, startY, startLeft, startTop;

        handle.addEventListener('mousedown', startDragging);
        document.addEventListener('mousemove', drag);
        document.addEventListener('mouseup', stopDragging);

        function startDragging(e) {
            isDragging = true;
            startX = e.clientX;
            startY = e.clientY;
            startLeft = parseInt(container.style.left) || 0;
            startTop = parseInt(container.style.top) || 0;
            e.preventDefault();
        }

        function drag(e) {
            if (!isDragging) return;
            if (container.dataset.pinned === 'true') return;
            const deltaX = e.clientX - startX;
            const deltaY = e.clientY - startY;
            container.style.left = `${startLeft + deltaX}px`;
            container.style.top = `${startTop + deltaY}px`;
            savePosition(startLeft + deltaX, startTop + deltaY);
        }

        function stopDragging() {
            isDragging = false;
        }
    }

    function savePosition(x, y) {
        GM_setValue("containerX", x);
        GM_setValue("containerY", y);
    }

    function loadPosition(container) {
        const x = GM_getValue("containerX", window.innerWidth - 260);
        const y = GM_getValue("containerY", window.innerHeight - 300);
        container.style.left = `${x}px`;
        container.style.top = `${y}px`;
    }

    function updateButtonsGrid() {
        const buttonGrid = document.getElementById('buttons-grid');
        buttonGrid.innerHTML = '';
        Object.entries(prompts).forEach(([name, prompt], index) => {
            const button = createButton(name, prompt, colors[index % colors.length]);
            buttonGrid.appendChild(button);
        });
        GM_setValue("savedPrompts", prompts);
    }

    function addPromptButtons() {
        const container = document.createElement('div');
        container.id = 'prompt-buttons-container';

        const dragHandle = document.createElement('div');
        dragHandle.id = 'drag-handle';

        const showHideBtn = document.createElement('button');
        showHideBtn.id = 'show-hide-btn';
        showHideBtn.textContent = 'Hide';
        showHideBtn.onclick = toggleButtons;

        const manageBtn = document.createElement('button');
        manageBtn.id = 'manage-btn';
        manageBtn.textContent = 'Manage';
        manageBtn.onclick = toggleManagePanel;

        const pinBtn = document.createElement('button');
        pinBtn.id = 'pin-btn';
        pinBtn.innerHTML = '📌';
        pinBtn.onclick = togglePin;

        dragHandle.appendChild(showHideBtn);
        dragHandle.appendChild(manageBtn);
        dragHandle.appendChild(pinBtn);

        const buttonGrid = document.createElement('div');
        buttonGrid.id = 'buttons-grid';

        const managePanel = createManagePanel();

        container.appendChild(dragHandle);
        container.appendChild(buttonGrid);
        container.appendChild(managePanel);
        document.body.appendChild(container);

        updateButtonsGrid();
        makeDraggable(container, dragHandle);
        loadPosition(container);
    }

    function toggleButtons() {
        const buttonGrid = document.getElementById('buttons-grid');
        const showHideBtn = document.getElementById('show-hide-btn');
        if (buttonGrid.style.display === 'none') {
            buttonGrid.style.display = 'grid';
            showHideBtn.textContent = 'Hide';
        } else {
            buttonGrid.style.display = 'none';
            showHideBtn.textContent = 'Show';
        }
    }

    function togglePin() {
        const container = document.getElementById('prompt-buttons-container');
        const pinBtn = document.getElementById('pin-btn');
        if (container.dataset.pinned === 'true') {
            delete container.dataset.pinned;
            pinBtn.style.opacity = '1';
        } else {
            container.dataset.pinned = 'true';
            pinBtn.style.opacity = '0.5';
        }
    }

    function toggleManagePanel() {
        const managePanel = document.getElementById('manage-panel');
        managePanel.style.display = managePanel.style.display === 'none' ? 'block' : 'none';
    }

    function createManagePanel() {
        const panel = document.createElement('div');
        panel.id = 'manage-panel';
        panel.style.display = 'none';

        const nameInput = document.createElement('input');
        nameInput.placeholder = 'Prompt Name';

        const promptInput = document.createElement('textarea');
        promptInput.placeholder = 'Prompt Text';

        const addButton = document.createElement('button');
        addButton.textContent = 'Add';
        addButton.onclick = () => {
            if (nameInput.value && promptInput.value) {
                prompts[nameInput.value] = promptInput.value;
                updateButtonsGrid();
                nameInput.value = '';
                promptInput.value = '';
            }
        };

        const editButton = document.createElement('button');
        editButton.textContent = 'Edit';
        editButton.onclick = () => {
            if (nameInput.value && promptInput.value && prompts.hasOwnProperty(nameInput.value)) {
                prompts[nameInput.value] = promptInput.value;
                updateButtonsGrid();
            }
        };

        const removeButton = document.createElement('button');
        removeButton.textContent = 'Remove';
        removeButton.onclick = () => {
            if (prompts.hasOwnProperty(nameInput.value)) {
                delete prompts[nameInput.value];
                updateButtonsGrid();
                nameInput.value = '';
                promptInput.value = '';
            }
        };

        panel.appendChild(nameInput);
        panel.appendChild(promptInput);
        panel.appendChild(addButton);
        panel.appendChild(editButton);
        panel.appendChild(removeButton);

        return panel;
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', addPromptButtons);
    } else {
        addPromptButtons();
    }
})();