Promptimizer

AI-powered prompt optimization tool that works with OpenAI-compatible APIs

// ==UserScript==
// @name         Promptimizer
// @namespace    https://github.com/NoahTheGinger/Promptimizer/
// @version      1.2
// @description  AI-powered prompt optimization tool that works with OpenAI-compatible APIs
// @author       NoahTheGinger
// @namespace    https://github.com/NoahTheGinger/Promptimizer/
// @match        *://*/*
// @license      MIT
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    // Styles for the UI
    GM_addStyle(`
        #promptimizer-container {
            position: fixed;
            /*
               UI CHANGE:
               Default position now near bottom-right.
               This corresponds with the toggle's new position in the bottom-right corner.
            */
            bottom: 80px;
            right: 20px;
            width: 400px;
            height: auto; /* Let container height auto-adjust initially */
            background: #ffffff;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.3);
            z-index: 2147483647;
            font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
            display: none;
            color: #000000;
            /* UI CHANGE: Added transform and opacity transitions for smoother show/hide + repositioning. */
            transition:
                opacity 0.2s ease,
                width 0.2s ease,
                height 0.2s ease,
                top 0.2s ease,
                left 0.2s ease,
                right 0.2s ease,
                bottom 0.2s ease;
            overflow: hidden;
            min-height: 300px;
            min-width: 300px;
            opacity: 0; /* For fade effect upon showing */
        }
        /* UI CHANGE: A "show" class that toggles visibility with a fade */
        #promptimizer-container.show {
            opacity: 1;
        }

        #promptimizer-header {
            padding: 12px;
            background: #2196F3;
            border-radius: 8px 8px 0 0;
            cursor: move;
            display: flex;
            justify-content: space-between;
            align-items: center;
            color: #ffffff;
            user-select: none; /* UI CHANGE: Prevent text selection while dragging header */
        }

        #promptimizer-header-controls {
            display: flex;
            gap: 12px;
            align-items: center;
        }

        .header-button {
            background: none;
            border: none;
            color: #ffffff;
            cursor: pointer;
            font-size: 16px;
            padding: 0;
            display: flex;
            align-items: center;
            opacity: 0.8;
            transition: opacity 0.2s;
        }

        .header-button:hover {
            opacity: 1;
        }

        #promptimizer-title {
            margin: 0;
            font-size: 16px;
            font-weight: bold;
            color: #ffffff;
        }

        #promptimizer-toggle {
            position: fixed;
            /*
               UI CHANGE:
               Toggle button now in bottom-right corner.
            */
            bottom: 20px;
            right: 20px;
            width: 50px;
            height: 50px;
            background: #2196F3;
            border-radius: 25px;
            color: white;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            font-size: 24px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
            z-index: 2147483647;
            border: 2px solid rgba(255,255,255,0.2);
        }

        #promptimizer-content {
            display: flex;
            flex-direction: column;
            background: #ffffff;
            overflow: hidden;
            max-height: calc(100% - 48px);
        }

        .scrollable-flex {
            overflow-y: auto;
            flex: 1;
            padding: 16px;
        }

        .promptimizer-input {
            width: 100%;
            padding: 8px;
            margin-bottom: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            box-sizing: border-box;
            background: #ffffff;
            color: #000000;
        }

        textarea.promptimizer-input {
            resize: none;
            min-height: 100px;
            transition: border-color 0.2s ease, box-shadow 0.2s ease;
            overflow-y: auto;
        }

        textarea.promptimizer-input:focus {
            border-color: #2196F3;
            box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.2);
            outline: none;
        }

        .prompt-type-select {
            width: 100%;
            padding: 8px;
            margin-bottom: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            background: #ffffff;
            color: #000000;
            font-size: 14px;
            cursor: pointer;
        }

        .prompt-type-select:focus {
            border-color: #2196F3;
            outline: none;
        }

        .input-group {
            margin-bottom: 16px;
        }

        .input-label {
            display: block;
            margin-bottom: 6px;
            color: #000000;
            font-size: 14px;
            font-weight: 500;
        }

        .promptimizer-button {
            background: #2196F3;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            margin-bottom: 10px;
            width: 100%;
        }

        .promptimizer-button:hover {
            background: #1976D2;
        }

        #promptimizer-response-container {
            position: relative;
            margin-top: 16px;
        }

        #promptimizer-response {
            margin-top: 0;
            padding: 12px;
            background: #f8f9fa;
            border-radius: 4px;
            white-space: pre-wrap;
            max-height: 300px;
            overflow-y: auto;
            color: #000000;
            border: 1px solid #e9ecef;
        }

        #copy-button {
            position: absolute;
            top: 8px;
            right: 8px;
            background: #2196F3;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 4px 8px;
            font-size: 12px;
            cursor: pointer;
            opacity: 0;
            transition: opacity 0.2s;
            z-index: 1;
        }

        #copy-button:hover {
            background: #1976D2;
        }

        #copy-button.visible {
            opacity: 1;
        }

        #copy-button.copied {
            background: #4CAF50;
        }

        .promptimizer-error {
            color: #dc3545;
            margin-top: 8px;
            font-size: 14px;
            background: #fff;
            padding: 8px;
            border-radius: 4px;
        }

        .config-section {
            margin-bottom: 15px;
            overflow: hidden;
            transition: max-height 0.3s ease-out;
        }

        .config-section.collapsed {
            max-height: 40px;
        }

        .config-section.expanded {
            max-height: 300px;
        }

        .config-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: pointer;
            padding: 8px 0;
        }

        .config-header h3 {
            margin: 0;
            font-size: 16px;
            color: #333;
        }

        .config-toggle {
            font-size: 18px;
            color: #666;
            transition: transform 0.3s;
        }

        .config-toggle.collapsed {
            transform: rotate(-90deg);
        }

        .config-content {
            transition: opacity 0.3s;
        }

        .config-content.collapsed {
            opacity: 0;
            height: 0;
            overflow: hidden;
        }

        .config-content.expanded {
            opacity: 1;
            height: auto;
        }

        /*
           UI CHANGE:
           Previously, only top-left resizing was available (.resize-handle-left, .resize-handle-top, .resize-handle-corner).
           Now we add handles on all sides + both corners (top-left & bottom-right) for a more standard resizing experience.
        */
        .resize-handle {
            position: absolute;
            background: transparent;
            z-index: 10;
        }
        /* Existing top-left handles */
        .resize-handle-left {
            cursor: ew-resize;
            width: 8px;
            height: 100%;
            left: 0;
            top: 0;
        }
        .resize-handle-top {
            cursor: ns-resize;
            height: 8px;
            width: 100%;
            top: 0;
            left: 0;
        }
        .resize-handle-corner {
            cursor: nwse-resize;
            width: 14px;
            height: 14px;
            left: 0;
            top: 0;
        }

        /* NEW bottom-right corner handle */
        .resize-handle-corner-br {
            cursor: nwse-resize;
            width: 14px;
            height: 14px;
            right: 0;
            bottom: 0;
        }
        /* NEW right-only handle */
        .resize-handle-right {
            cursor: ew-resize;
            width: 8px;
            height: 100%;
            top: 0;
            right: 0;
        }
        /* NEW bottom-only handle */
        .resize-handle-bottom {
            cursor: ns-resize;
            height: 8px;
            width: 100%;
            bottom: 0;
            left: 0;
        }

        #promptimizer-container.resizing {
            transition: none;
            box-shadow: 0 2px 15px rgba(33, 150, 243, 0.4);
        }

        #promptimizer-container.resizing-left {
            border-left: 2px solid #2196F3;
        }
        #promptimizer-container.resizing-top {
            border-top: 2px solid #2196F3;
        }
        #promptimizer-container.resizing-corner {
            border-left: 2px solid #2196F3;
            border-top: 2px solid #2196F3;
        }

        /* UI CHANGE: Additional highlighting classes for new handles. */
        #promptimizer-container.resizing-right {
            border-right: 2px solid #2196F3;
        }
        #promptimizer-container.resizing-bottom {
            border-bottom: 2px solid #2196F3;
        }
        #promptimizer-container.resizing-corner-br {
            border-right: 2px solid #2196F3;
            border-bottom: 2px solid #2196F3;
        }
    `);

    // Default dimensions and position
    const defaultDimensions = {
        // UI CHANGE: Adjusted to bottom-right defaults
        width: '400px',
        height: 'auto',
        bottom: '80px',
        right: '20px'
    };

    // Create UI elements
    function createUI() {
        // Toggle button
        const toggle = document.createElement('div');
        toggle.id = 'promptimizer-toggle';
        toggle.innerHTML = '✨';
        toggle.title = 'Toggle Promptimizer';
        document.body.appendChild(toggle);

        // Main container
        const container = document.createElement('div');
        container.id = 'promptimizer-container';
        container.innerHTML = `
            <div id="promptimizer-header">
                <h2 id="promptimizer-title">Promptimizer</h2>
                <div id="promptimizer-header-controls">
                    <button class="header-button" id="promptimizer-reset" title="Reset UI Position and Size">↺</button>
                    <button class="header-button" id="promptimizer-minimize" title="Minimize">−</button>
                </div>
            </div>
            <div id="promptimizer-content">
              <div class="scrollable-flex">
                <div class="config-section expanded" id="config-section">
                    <div class="config-header" id="config-header">
                        <h3>API Configuration</h3>
                        <span class="config-toggle">▼</span>
                    </div>
                    <div class="config-content expanded">
                        <input type="text" id="api-url" class="promptimizer-input" placeholder="API URL (e.g., https://api.openai.com/v1)" />
                        <input type="password" id="api-key" class="promptimizer-input" placeholder="API Key" />
                        <input type="text" id="model-name" class="promptimizer-input" placeholder="Model name" />
                        <input type="text" id="provider-name" class="promptimizer-input" placeholder="Provider name (optional, for gpt4free)" />
                        <button id="save-config" class="promptimizer-button">Save Configuration</button>
                    </div>
                </div>
                <div class="input-group">
                    <label class="input-label" for="prompt-type">Prompt Type:</label>
                    <select id="prompt-type" class="prompt-type-select">
                        <option value="user">User Prompt</option>
                        <option value="system">System Prompt</option>
                    </select>
                </div>
                <div class="input-group">
                    <label class="input-label" for="prompt-input">Enter Your Prompt:</label>
                    <textarea id="prompt-input" class="promptimizer-input" rows="4" placeholder="Enter your prompt here..."></textarea>
                </div>
                <button id="optimize-button" class="promptimizer-button">Optimize Prompt</button>
                <div id="promptimizer-response-container">
                    <button id="copy-button" style="display: none;">Copy Enhanced Prompt</button>
                    <div id="promptimizer-response"></div>
                </div>
              </div>
            </div>
            <!-- UI CHANGE: A complete set of resize handles: top-left corner, top, left, bottom-right corner, right, bottom. -->
            <div class="resize-handle resize-handle-left"></div>
            <div class="resize-handle resize-handle-top"></div>
            <div class="resize-handle resize-handle-corner"></div>
            <div class="resize-handle resize-handle-right"></div>
            <div class="resize-handle resize-handle-bottom"></div>
            <div class="resize-handle resize-handle-corner-br"></div>
        `;
        document.body.appendChild(container);

        // Make the container draggable (UI only)
        makeDraggable(container);

        // Add container resizing
        makeResizable(container);

        // Load saved configuration
        loadConfiguration();

        // Event listeners
        // UI CHANGE: Now using a smoother show/hide approach with fade
        toggle.addEventListener('click', () => {
            if (container.style.display === 'none' || container.style.display === '') {
                container.style.display = 'block';
                // Force reflow, then add .show for fade-in
                requestAnimationFrame(() => container.classList.add('show'));
            } else {
                // Remove .show for fade-out
                container.classList.remove('show');
                // Wait for transition to complete, then hide
                setTimeout(() => {
                    if (!container.classList.contains('show')) {
                        container.style.display = 'none';
                    }
                }, 200);
            }
        });

        document.getElementById('promptimizer-minimize').addEventListener('click', () => {
            // Fade out on minimize
            container.classList.remove('show');
            setTimeout(() => {
                if (!container.classList.contains('show')) {
                    container.style.display = 'none';
                }
            }, 200);
        });

        // Config section toggle
        const configHeader = document.getElementById('config-header');
        const configSection = document.getElementById('config-section');
        const configToggle = configHeader.querySelector('.config-toggle');
        const configContent = configSection.querySelector('.config-content');

        configHeader.addEventListener('click', () => {
            const isExpanded = configSection.classList.contains('expanded');

            if (isExpanded) {
                configSection.classList.remove('expanded');
                configSection.classList.add('collapsed');
                configContent.classList.remove('expanded');
                configContent.classList.add('collapsed');
                configToggle.classList.add('collapsed');
            } else {
                configSection.classList.remove('collapsed');
                configSection.classList.add('expanded');
                configContent.classList.remove('collapsed');
                configContent.classList.add('expanded');
                configToggle.classList.remove('collapsed');
            }
        });

        document.getElementById('save-config').addEventListener('click', saveConfiguration);
        document.getElementById('optimize-button').addEventListener('click', optimizePrompt);
        document.getElementById('copy-button').addEventListener('click', copyEnhancedPrompt);

        // Initialize auto-height for textarea
        const textarea = document.getElementById('prompt-input');
        if (textarea) {
            autoResizeTextarea(textarea);
            textarea.addEventListener('input', function() {
                autoResizeTextarea(this);
            });
        }
    }

    // Make an element draggable (UI only; no functional logic changed)
    function makeDraggable(element) {
        const header = element.querySelector('#promptimizer-header');
        let isDragging = false;
        let startX, startY, offsetX, offsetY;

        // Function to reset position and size
        function resetPosition() {
            // Remove transform usage; set position explicitly
            element.style.width = defaultDimensions.width;
            element.style.height = defaultDimensions.height;
            element.style.bottom = defaultDimensions.bottom;
            element.style.right = defaultDimensions.right;
            element.style.top = 'auto';
            element.style.left = 'auto';

            // Reset any set textarea dimensions
            const textarea = document.getElementById('prompt-input');
            if (textarea) {
                textarea.style.height = '';
                autoResizeTextarea(textarea);
            }
        }

        // Add reset button event listener
        document.getElementById('promptimizer-reset').addEventListener('click', (e) => {
            e.stopPropagation(); // Prevent initiating a drag
            resetPosition();
        });

        header.addEventListener('mousedown', dragStart);
        document.addEventListener('mousemove', drag);
        document.addEventListener('mouseup', dragEnd);

        function dragStart(e) {
            if (e.target.closest('.header-button')) {
                return;
            }
            isDragging = true;
            // Compute how far from the element's top-left corner the pointer is
            const rect = element.getBoundingClientRect();
            startX = rect.left;
            startY = rect.top;
            offsetX = e.clientX - startX;
            offsetY = e.clientY - startY;

            // Disable transition during actual drag for a crisper effect
            element.style.transition = 'none';
        }

        function drag(e) {
            if (!isDragging) return;
            e.preventDefault();

            // Figure out the new top-left based on pointer
            let newLeft = e.clientX - offsetX;
            let newTop = e.clientY - offsetY;

            // Keep within viewport, if desired
            const rect = element.getBoundingClientRect();
            const w = rect.width;
            const h = rect.height;
            const vw = window.innerWidth;
            const vh = window.innerHeight;

            // Adjust so it won't go offscreen
            if (newLeft < 0) newLeft = 0;
            if (newTop < 0) newTop = 0;
            if (newLeft + w > vw) newLeft = vw - w;
            if (newTop + h > vh) newTop = vh - h;

            // Clear bottom/right so we can use top/left
            element.style.bottom = 'auto';
            element.style.right = 'auto';

            // Apply new position
            element.style.left = newLeft + 'px';
            element.style.top = newTop + 'px';
        }

        function dragEnd() {
            isDragging = false;
            // Re-enable transitions for nice layout changes after drag
            element.style.transition =
                'opacity 0.2s ease, width 0.2s ease, height 0.2s ease, top 0.2s ease, left 0.2s ease, right 0.2s ease, bottom 0.2s ease';
        }

        // Initialize at default bottom-right position
        resetPosition();
    }

    // Make container resizable (UI only)
    function makeResizable(container) {
        const leftHandle = container.querySelector('.resize-handle-left');
        const topHandle = container.querySelector('.resize-handle-top');
        const cornerTLHandle = container.querySelector('.resize-handle-corner');  // top-left corner
        // UI CHANGE: Additional handles
        const rightHandle = container.querySelector('.resize-handle-right');
        const bottomHandle = container.querySelector('.resize-handle-bottom');
        const cornerBRHandle = container.querySelector('.resize-handle-corner-br');

        let isResizing = false;
        let currentResizeType = '';
        let startX, startY, startWidth, startHeight, startTop, startLeft, startBottom, startRight;

        function startResize(e, type) {
            e.preventDefault();
            e.stopPropagation();

            isResizing = true;
            currentResizeType = type;

            const rect = container.getBoundingClientRect();
            startWidth = rect.width;
            startHeight = rect.height;
            startTop = rect.top;
            startLeft = rect.left;
            startBottom = window.innerHeight - rect.bottom;  // distance from bottom to viewport
            startRight = window.innerWidth - rect.right;     // distance from right to viewport
            startX = e.clientX;
            startY = e.clientY;

            container.classList.add('resizing');
            container.classList.add(`resizing-${type}`);

            document.addEventListener('mousemove', resize);
            document.addEventListener('mouseup', stopResize);

            // Disable transitions during actual resize
            container.style.transition = 'none';
        }

        function resize(e) {
            if (!isResizing) return;

            let newWidth = startWidth;
            let newHeight = startHeight;
            let newTop = startTop;
            let newLeft = startLeft;
            let newRight = startRight;
            let newBottom = startBottom;

            const dx = e.clientX - startX;
            const dy = e.clientY - startY;

            switch (currentResizeType) {
                case 'left':
                    newWidth = startWidth - dx;
                    newLeft = startLeft + dx;
                    break;
                case 'top':
                    newHeight = startHeight - dy;
                    newTop = startTop + dy;
                    break;
                case 'corner': // top-left corner
                    newWidth = startWidth - dx;
                    newHeight = startHeight - dy;
                    newLeft = startLeft + dx;
                    newTop = startTop + dy;
                    break;
                // UI CHANGE: Additional cases for new handles
                case 'right':
                    // Resizing from the right edge outward
                    newWidth = startWidth + dx;
                    break;
                case 'bottom':
                    // Resizing from the bottom edge downward
                    newHeight = startHeight + dy;
                    break;
                case 'corner-br': // bottom-right corner
                    newWidth = startWidth + dx;
                    newHeight = startHeight + dy;
                    break;
            }

            // Enforce minimum dimensions
            if (newWidth < 300) {
                newWidth = 300;
                // If resizing from left or top-left corner, shift left to keep min width
                if (['left','corner'].includes(currentResizeType)) {
                    newLeft = startLeft + (startWidth - 300);
                }
                // If resizing from right or bottom-right corner, we just clamp
            }
            if (newHeight < 300) {
                newHeight = 300;
                // If resizing from top or top-left corner, shift top
                if (['top','corner'].includes(currentResizeType)) {
                    newTop = startTop + (startHeight - 300);
                }
            }

            // Keep within viewport (optional)
            const vw = window.innerWidth;
            const vh = window.innerHeight;
            if (['left','corner'].includes(currentResizeType) && newLeft < 0) {
                newLeft = 0;
            }
            if (['top','corner'].includes(currentResizeType) && newTop < 0) {
                newTop = 0;
            }
            if (['right','corner-br'].includes(currentResizeType) && (startLeft + newWidth > vw)) {
                newWidth = vw - startLeft;
            }
            if (['bottom','corner-br'].includes(currentResizeType) && (startTop + newHeight > vh)) {
                newHeight = vh - startTop;
            }

            // Apply geometry. For bottom/right-based resizing, keep the container's top/left unless
            // we're specifically resizing from top or left.
            container.style.width = newWidth + 'px';
            container.style.height = newHeight + 'px';

            // If resizing from left or top or top-left corner, update top/left explicitly
            if (['left','top','corner'].includes(currentResizeType)) {
                container.style.left = newLeft + 'px';
                container.style.top = newTop + 'px';
                // Clear bottom/right so they won't conflict
                container.style.bottom = 'auto';
                container.style.right = 'auto';
            }
        }

        function stopResize() {
            isResizing = false;
            container.classList.remove('resizing');
            container.classList.remove(`resizing-${currentResizeType}`);

            document.removeEventListener('mousemove', resize);
            document.removeEventListener('mouseup', stopResize);

            // Re-enable transitions
            container.style.transition =
                'opacity 0.2s ease, width 0.2s ease, height 0.2s ease, top 0.2s ease, left 0.2s ease, right 0.2s ease, bottom 0.2s ease';
        }

        leftHandle.addEventListener('mousedown', (e) => startResize(e, 'left'));
        topHandle.addEventListener('mousedown', (e) => startResize(e, 'top'));
        cornerTLHandle.addEventListener('mousedown', (e) => startResize(e, 'corner'));
        // UI CHANGE: New handles
        rightHandle.addEventListener('mousedown', (e) => startResize(e, 'right'));
        bottomHandle.addEventListener('mousedown', (e) => startResize(e, 'bottom'));
        cornerBRHandle.addEventListener('mousedown', (e) => startResize(e, 'corner-br'));
    }

    // Helper function to auto-resize textarea based on content
    function autoResizeTextarea(textarea) {
        // Save scroll position
        const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
        // Reset height temporarily to get accurate scrollHeight
        textarea.style.height = 'auto';
        // Set new height based on content (with minimum)
        const newHeight = Math.max(textarea.scrollHeight, 100) + 'px';
        textarea.style.transition = 'height 0.15s ease-out';
        textarea.style.height = newHeight;
        // Restore scroll position to prevent page jump
        window.scrollTo(0, scrollTop);
    }

    // Save API configuration
    function saveConfiguration() {
        const apiUrl = document.getElementById('api-url').value;
        const apiKey = document.getElementById('api-key').value;
        const modelName = document.getElementById('model-name').value;
        const providerName = document.getElementById('provider-name').value;

        GM_setValue('apiUrl', apiUrl);
        GM_setValue('apiKey', apiKey);
        GM_setValue('modelName', modelName);
        GM_setValue('providerName', providerName);

        showResponse('Configuration saved!');
    }

    // Load saved configuration
    function loadConfiguration() {
        const apiUrl = GM_getValue('apiUrl', '');
        const apiKey = GM_getValue('apiKey', '');
        const modelName = GM_getValue('modelName', '');
        const providerName = GM_getValue('providerName', '');

        document.getElementById('api-url').value = apiUrl;
        document.getElementById('api-key').value = apiKey;
        document.getElementById('model-name').value = modelName;
        document.getElementById('provider-name').value = providerName;
    }

    // Show response or error message
    function showResponse(message, isError = false) {
        const responseDiv = document.getElementById('promptimizer-response');
        const copyButton = document.getElementById('copy-button');
        responseDiv.textContent = message;
        responseDiv.className = isError ? 'promptimizer-error' : '';

        if (!isError && message !== 'Optimizing prompt...') {
            copyButton.style.display = 'block';
            setTimeout(() => copyButton.classList.add('visible'), 10);
        } else {
            copyButton.style.display = 'none';
            copyButton.classList.remove('visible');
        }
    }

    // Extract enhanced prompt from response
    function extractEnhancedPrompt(text) {
        const match = text.match(/<enhanced_prompt>([\s\S]*?)<\/enhanced_prompt>/);
        return match ? match[1].trim() : text;
    }

    // Copy enhanced prompt to clipboard
    function copyEnhancedPrompt() {
        const responseDiv = document.getElementById('promptimizer-response');
        const copyButton = document.getElementById('copy-button');
        const textToCopy = extractEnhancedPrompt(responseDiv.textContent);

        navigator.clipboard.writeText(textToCopy).then(() => {
            copyButton.textContent = 'Copied!';
            copyButton.classList.add('copied');
            setTimeout(() => {
                copyButton.textContent = 'Copy Enhanced Prompt';
                copyButton.classList.remove('copied');
            }, 2000);
        }).catch(err => {
            console.error('Failed to copy text: ', err);
        });
    }

    // Optimize prompt using the API
    async function optimizePrompt() {
        const apiUrl = GM_getValue('apiUrl', '');
        const apiKey = GM_getValue('apiKey', '');
        const modelName = GM_getValue('modelName', '');
        const providerName = GM_getValue('providerName', '');
        const promptType = document.getElementById('prompt-type').value;
        const promptInput = document.getElementById('prompt-input').value;

        if (!apiUrl || !apiKey || !modelName) {
            showResponse('Please configure API settings first!', true);
            return;
        }

        if (!promptInput.trim()) {
            showResponse('Please enter a prompt to optimize!', true);
            return;
        }

        showResponse('Optimizing prompt...');

        const systemPrompt = `You are a specialized prompt optimization AI focused on enhancing both user prompts and system prompts for AI interactions.

<instructions>
1. You will receive prompts marked with either <user_prompt> or <system_prompt> tags
2. ALWAYS maintain the same type of prompt in your enhancement
3. NEVER engage in conversation or provide explanations
4. ONLY return the enhanced prompt within <enhanced_prompt> tags
5. Apply appropriate prompt engineering techniques based on the prompt type:

   For User Prompts:
   - Maintain conversational context and flow
   - Clarify user intent and expectations
   - Add specific parameters for the response
   - Include relevant context from prior conversation
   - Structure multi-part requests clearly

   For System Prompts:
   - Define clear roles and responsibilities
   - Establish behavioral boundaries
   - Include success criteria and constraints
   - Structure hierarchical instructions
   - Define interaction patterns
   - Specify output formats and preferences
   - Include error handling instructions
</instructions>

<examples>
<example>
<input_type>user_prompt</input_type>
<input>Thanks for your help! Can you search the web for more information about this topic?</input>
<enhanced_prompt>Please conduct a comprehensive web search on our current topic with the following parameters:
1. Focus on authoritative sources from the last 2 years
2. Include academic and expert perspectives
3. Compare and contrast different viewpoints
4. Identify emerging trends and developments
5. Extract key insights and practical applications

Format the response with:
- Main findings in bullet points
- Source citations for each major claim
- Relevance assessment for each source
- Synthesis of conflicting information
- Suggestions for further research</enhanced_prompt>
</example>

<example>
<input_type>system_prompt</input_type>
<input>You are a web search AI assistant. Your role is to help users find information.</input>
<enhanced_prompt>You are a specialized web search AI assistant designed to provide comprehensive, accurate, and well-structured information retrieval services.

Core Responsibilities:
1. Execute precise web searches based on user queries
2. Evaluate source credibility and relevance
3. Synthesize information from multiple sources
4. Present findings in clear, structured formats

Behavioral Guidelines:
- Maintain objectivity in information presentation
- Clearly distinguish between facts and interpretations
- Acknowledge information gaps or uncertainties
- Proactively suggest related topics for exploration

Output Requirements:
1. Structure all responses with:
   - Executive summary
   - Detailed findings
   - Source citations
   - Confidence levels
2. Use formatting for clarity:
   - Bullet points for key facts
   - Tables for comparisons
   - Markdown for emphasis
   - Hierarchical headings

Error Handling:
- Acknowledge when information is outdated
- Flag potential misinformation
- Suggest alternative search strategies
- Provide confidence levels for findings</enhanced_prompt>
</example>
</examples>

<success_criteria>
For User Prompts:
- Enhanced clarity and specificity
- Maintained conversation context
- Clear parameters for response
- Structured multi-part requests
- Defined output preferences

For System Prompts:
- Clear role definition
- Comprehensive behavioral guidelines
- Specific output requirements
- Error handling procedures
- Interaction patterns defined
</success_criteria>`;

        // Wrap the input with appropriate type tags
        const taggedInput = `<${promptType}_prompt>${promptInput}</${promptType}_prompt>`;

        GM_xmlhttpRequest({
            method: 'POST',
            url: `${apiUrl}/chat/completions`,
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${apiKey}`
            },
            data: JSON.stringify({
                model: modelName,
                ...(providerName && { provider: providerName }),
                messages: [
                    {
                        role: 'system',
                        content: systemPrompt
                    },
                    {
                        role: 'user',
                        content: taggedInput
                    }
                ],
                temperature: 0.7,
                max_tokens: 16384
            }),
            onload: function(response) {
                try {
                    const result = JSON.parse(response.responseText);
                    if (result.error) {
                        showResponse(`Error: ${result.error.message}`, true);
                    } else if (result.choices && result.choices[0]) {
                        showResponse(result.choices[0].message.content);
                    } else {
                        showResponse('Unexpected API response format', true);
                    }
                } catch (error) {
                    showResponse(`Error processing response: ${error.message}`, true);
                }
            },
            onerror: function(error) {
                showResponse(`Network error: ${error.statusText}`, true);
            }
        });
    }

    // Initialize the UI
    createUI();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址