Grok Imagine Manager

Prevents duplicate tracking. Video tab now includes Sort by High/Low Percentage.

От 19.11.2025. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Grok Imagine Manager
// @namespace    http://tampermonkey.net/
// @version      3.8
// @description  Prevents duplicate tracking. Video tab now includes Sort by High/Low Percentage.
// @author       You
// @license MIT
// @match        https://grok.com/*
// @match        https://*.grok.com/*
// @match        https://grok.x.ai/*
// @match        https://*.grok.x.ai/*
// @match        https://x.com/*
// @match        https://*.x.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    if (window.grokPromptManagerLoaded) return;
    window.grokPromptManagerLoaded = true;

    function initScript() {
        // --- MODERN CSS ---
        GM_addStyle(`
            :root {
                --grok-bg: #000000;
                --grok-surface: #16181c;
                --grok-surface-hover: #1d1f23;
                --grok-border: #2f3336;
                --grok-primary: #1d9bf0;
                --grok-primary-hover: #1a8cd8;
                --grok-text-main: #e7e9ea;
                --grok-text-muted: #71767b;
                --grok-danger: #f4212e;
                --grok-warning: #ffd400;
                --grok-success: #00ba7c;
                --grok-radius: 16px;
                --grok-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
            }

            .grok-prompt-overlay { position: fixed; inset: 0; z-index: 10000; background: rgba(0, 0, 0, 0.4); backdrop-filter: blur(4px); display: none; opacity: 0; transition: opacity 0.2s ease; }
            .grok-prompt-overlay.open { display: flex; opacity: 1; pointer-events: auto; }
            .grok-prompt-modal { position: fixed; background: rgba(22, 24, 28, 0.95); border: 1px solid var(--grok-border); border-radius: var(--grok-radius); width: 850px; height: 650px; min-width: 500px; min-height: 400px; display: flex; flex-direction: column; box-shadow: var(--grok-shadow); top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.95); transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1); color: var(--grok-text-main); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }
            .grok-prompt-overlay.open .grok-prompt-modal { transform: translate(-50%, -50%) scale(1); }
            .grok-prompt-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 24px; border-bottom: 1px solid var(--grok-border); cursor: move; background: rgba(255,255,255,0.02); }
            .grok-prompt-title { font-size: 18px; font-weight: 700; display: flex; align-items: center; gap: 10px; }
            .grok-prompt-close { background: transparent; border: none; color: var(--grok-text-muted); cursor: pointer; padding: 8px; border-radius: 50%; transition: all 0.2s; }
            .grok-prompt-close:hover { background: rgba(239, 68, 68, 0.1); color: var(--grok-danger); }
            .grok-prompt-tabs { display: flex; padding: 0 16px; border-bottom: 1px solid var(--grok-border); background: var(--grok-bg); }
            .grok-prompt-tab { padding: 16px; background: none; border: none; color: var(--grok-text-muted); font-weight: 600; font-size: 14px; cursor: pointer; border-bottom: 3px solid transparent; transition: all 0.2s; }
            .grok-prompt-tab:hover { color: var(--grok-text-main); background: rgba(255,255,255,0.03); }
            .grok-prompt-tab.active { color: var(--grok-primary); border-bottom-color: var(--grok-primary); }
            .grok-prompt-content { flex: 1; overflow-y: auto; padding: 24px; scroll-behavior: smooth; }
            .grok-prompt-content::-webkit-scrollbar { width: 8px; }
            .grok-prompt-content::-webkit-scrollbar-track { background: transparent; }
            .grok-prompt-content::-webkit-scrollbar-thumb { background: var(--grok-border); border-radius: 4px; }
            .grok-prompt-form { display: flex; flex-direction: column; gap: 20px; }
            .grok-prompt-label { display: block; font-size: 13px; font-weight: 600; color: var(--grok-text-muted); margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.5px; }
            .grok-prompt-select, .grok-prompt-textarea, .grok-prompt-category-input { width: 100%; background: black; border: 1px solid var(--grok-border); border-radius: 8px; padding: 12px; color: var(--grok-text-main); font-size: 15px; font-family: inherit; transition: border-color 0.2s, box-shadow 0.2s; box-sizing: border-box; }
            .grok-prompt-select:focus, .grok-prompt-textarea:focus, .grok-prompt-category-input:focus { outline: none; border-color: var(--grok-primary); box-shadow: 0 0 0 2px rgba(29, 155, 240, 0.2); }
            .grok-prompt-textarea { height: 140px; line-height: 1.5; resize: vertical; }
            .grok-prompt-button, .grok-prompt-add-btn { background: var(--grok-primary); color: white; border: none; padding: 12px 20px; border-radius: 24px; font-weight: 700; cursor: pointer; transition: transform 0.1s, background 0.2s; display: flex; align-items: center; justify-content: center; gap: 8px; }
            .grok-prompt-button:hover { background: var(--grok-primary-hover); transform: translateY(-1px); }
            .grok-prompt-list { display: flex; flex-direction: column; gap: 12px; }
            .grok-prompt-item { background: transparent; border: 1px solid var(--grok-border); border-radius: 12px; padding: 16px; transition: background 0.2s, border-color 0.2s; }
            .grok-prompt-item:hover { background: rgba(255,255,255,0.02); border-color: #555; }
            .grok-prompt-item-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 15px; margin-bottom: 10px; }
            .grok-prompt-item-text { flex: 1; color: var(--grok-text-main); line-height: 1.5; font-size: 15px; white-space: pre-wrap; }
            .grok-prompt-item-delete { opacity: 0; color: var(--grok-text-muted); background: none; border: none; cursor: pointer; transition: opacity 0.2s, color 0.2s; padding: 4px; }
            .grok-prompt-item:hover .grok-prompt-item-delete { opacity: 1; }
            .grok-prompt-item-delete:hover { color: var(--grok-danger); }

            .grok-prompt-item-footer { display: flex; align-items: center; gap: 12px; margin-top: 12px; flex-wrap: wrap; }
            .grok-prompt-category-badge { background: rgba(29, 155, 240, 0.1); color: var(--grok-primary); padding: 4px 10px; border-radius: 4px; font-size: 11px; font-weight: 700; text-transform: uppercase; flex-shrink: 0; }
            .grok-prompt-category-badge.auto { background: rgba(0, 186, 124, 0.1); color: var(--grok-success); }
            .grok-prompt-copy-btn { flex-shrink: 0; background: transparent; border: 1px solid var(--grok-border); color: var(--grok-text-muted); padding: 6px 12px; border-radius: 16px; font-size: 12px; font-weight: 600; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; transition: all 0.2s; }
            .grok-prompt-copy-btn:hover { border-color: var(--grok-text-main); color: var(--grok-text-main); background: rgba(255,255,255,0.05); }
            .grok-prompt-stars { display: flex; gap: 6px; }
            .grok-prompt-star { width: 32px; height: 32px; cursor: pointer; color: #333; transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1); }
            .grok-prompt-star.filled { color: #ffd700; filter: drop-shadow(0 0 2px rgba(255, 215, 0, 0.4)); }
            .grok-prompt-star:hover { transform: scale(1.15); }
            .grok-toast-container { position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%); display: flex; flex-direction: column; gap: 8px; z-index: 11000; pointer-events: none; }
            .grok-toast { background: var(--grok-primary); color: white; padding: 10px 20px; border-radius: 24px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); font-weight: 600; font-size: 14px; animation: slideUpFade 0.3s ease forwards; display: flex; align-items: center; gap: 8px; }
            @keyframes slideUpFade { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
            .grok-prompt-category-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
            .grok-prompt-category-tag { background: var(--grok-surface-hover); border: 1px solid var(--grok-border); color: var(--grok-text-main); padding: 8px 14px; border-radius: 20px; font-size: 13px; display: flex; align-items: center; gap: 8px; }
            .grok-prompt-export-section { margin-top: 32px; padding-top: 24px; border-top: 1px solid var(--grok-border); }
            .grok-prompt-export-btn { flex: 1; background: transparent; border: 1px solid var(--grok-border); color: var(--grok-text-main); padding: 12px; border-radius: 8px; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; transition: all 0.2s; font-weight: 600; }
            .grok-prompt-export-btn:hover { background: var(--grok-surface-hover); border-color: #666; }
            .grok-prompt-export-btn.danger { color: var(--grok-danger); border-color: rgba(244, 33, 46, 0.3); }
            .grok-prompt-export-btn.danger:hover { background: rgba(244, 33, 46, 0.1); border-color: var(--grok-danger); }
            .grok-prompt-hint { position: fixed; bottom: 20px; right: 20px; background: var(--grok-surface); border: 1px solid var(--grok-border); padding: 10px 16px; border-radius: 12px; color: var(--grok-text-muted); font-size: 13px; font-weight: 500; box-shadow: 0 10px 20px rgba(0,0,0,0.3); display: flex; align-items: center; gap: 10px; z-index: 9999; }
            .grok-prompt-kbd { background: #333; color: white; padding: 2px 6px; border-radius: 4px; font-family: monospace; font-size: 12px; border-bottom: 2px solid #111; }
            .grok-prompt-resize-handle { position: absolute; width: 20px; height: 20px; right: 0; bottom: 0; cursor: se-resize; z-index: 10; }

            /* --- FILTER & SORT BAR --- */
            .grok-control-bar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; flex-wrap: wrap; gap: 10px; }
            .grok-filter-group { display: flex; gap: 6px; flex-wrap: wrap; }
            .grok-sort-group { display: flex; gap: 4px; align-items: center; padding-left: 10px; border-left: 1px solid var(--grok-border); }

            .grok-prompt-filter-btn { background: transparent; border: 1px solid var(--grok-border); color: var(--grok-text-muted); padding: 6px 14px; border-radius: 20px; cursor: pointer; font-size: 13px; font-weight: 500; transition: 0.2s; }
            .grok-prompt-filter-btn.active { background: var(--grok-primary); border-color: var(--grok-primary); color: white; }

            .grok-prompt-sort-btn { background: transparent; border: none; color: var(--grok-text-muted); padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 11px; font-weight: 600; text-transform: uppercase; transition: 0.2s; }
            .grok-prompt-sort-btn:hover { color: var(--grok-text-main); background: rgba(255,255,255,0.05); }
            .grok-prompt-sort-btn.active { color: var(--grok-primary); background: rgba(29, 155, 240, 0.1); }

            .grok-checkbox-wrapper { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; padding: 16px; background: rgba(255,255,255,0.03); border-radius: 12px; }
            .grok-checkbox { width: 20px; height: 20px; accent-color: var(--grok-primary); cursor: pointer; }

            /* Video & Image Styles */
            .grok-video-section { margin-top: 12px; background: rgba(255, 255, 255, 0.03); padding: 10px; border-radius: 8px; border: 1px dashed var(--grok-border); }
            .grok-video-input { width: 100%; background: transparent; border: none; color: var(--grok-text-muted); font-size: 13px; font-family: inherit; resize: none; outline: none; height: 32px; transition: height 0.2s; }
            .grok-video-input:focus { color: var(--grok-text-main); height: 60px; }
            .grok-video-label { font-size: 11px; color: var(--grok-primary); font-weight: 700; text-transform: uppercase; margin-bottom: 4px; display: block; }
            .grok-video-save-hint { font-size: 10px; color: #555; text-align: right; display: none; }
            .grok-video-input:focus + .grok-video-save-hint { display: block; }

            /* Snapshot Grid Layout */
            .grok-image-item-grid { display: flex; gap: 16px; }
            .grok-image-content-col { flex: 1; }
            .grok-image-preview-col { width: 120px; display: flex; flex-direction: column; gap: 8px; align-items: center; justify-content: flex-start; }

            .grok-snapshot-thumb { width: 100%; height: 100px; object-fit: cover; border-radius: 8px; border: 1px solid var(--grok-border); cursor: zoom-in; transition: transform 0.2s; background: #000; }
            .grok-snapshot-thumb:hover { transform: scale(1.05); border-color: var(--grok-primary); }

            .grok-snapshot-upload-btn { position: relative; width: 100%; height: 100px; border: 1px dashed var(--grok-border); border-radius: 8px; display: flex; align-items: center; justify-content: center; flex-direction: column; color: var(--grok-text-muted); cursor: pointer; font-size: 12px; transition: 0.2s; background: rgba(255,255,255,0.02); }
            .grok-snapshot-upload-btn:hover { border-color: var(--grok-primary); color: var(--grok-primary); background: rgba(29, 155, 240, 0.05); }
            .grok-snapshot-input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }

            .grok-snapshot-del { background: rgba(0,0,0,0.7); color: white; border: none; border-radius: 50%; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; cursor: pointer; position: absolute; top: -5px; right: -5px; font-size: 12px; line-height: 1; }
            .grok-snapshot-wrapper { position: relative; width: 100%; }

            /* --- MODERATION SLIDER --- */
            .grok-mod-wrapper { display: flex; align-items: center; gap: 10px; flex-grow: 1; min-width: 140px; max-width: 300px; background: rgba(0,0,0,0.2); padding: 4px 10px; border-radius: 20px; border: 1px solid var(--grok-border); margin-right: 8px; }
            .grok-mod-label { font-size: 10px; text-transform: uppercase; color: var(--grok-text-muted); font-weight: 700; white-space: nowrap; }
            .grok-mod-slider { -webkit-appearance: none; appearance: none; flex: 1; height: 4px; background: #333; border-radius: 2px; outline: none; cursor: ew-resize; }
            .grok-mod-slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 14px; height: 14px; background: var(--grok-text-main); border-radius: 50%; cursor: pointer; transition: 0.2s; }
            .grok-mod-slider::-webkit-slider-thumb:hover { transform: scale(1.2); }
            .grok-mod-val { font-size: 12px; font-weight: 700; min-width: 35px; text-align: right; font-variant-numeric: tabular-nums; }
            .grok-mod-val.low { color: var(--grok-danger); }
            .grok-mod-val.med { color: var(--grok-warning); }
            .grok-mod-val.high { color: var(--grok-success); }

            /* Lightbox */
            .grok-lightbox { position: fixed; inset: 0; z-index: 12000; background: rgba(0,0,0,0.9); display: none; align-items: center; justify-content: center; cursor: zoom-out; }
            .grok-lightbox.open { display: flex; animation: fadeIn 0.2s; }
            .grok-lightbox img { max-width: 90%; max-height: 90%; border-radius: 4px; box-shadow: 0 0 50px rgba(0,0,0,0.5); }
            @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
        `);

        // --- STATE ---
        let isOpen = false;
        let currentRating = 0;
        let currentCategory = '';
        let filterCategory = 'all';
        let videoSortMode = 'newest'; // 'newest', 'high', 'low'
        let isDragging = false, isResizing = false;
        let dragOffset = { x: 0, y: 0 };
        let modalElement = null;

        // --- HELPERS ---
        function showToast(message, type = 'success') {
            let container = document.querySelector('.grok-toast-container');
            if (!container) {
                container = document.createElement('div');
                container.className = 'grok-toast-container';
                document.body.appendChild(container);
            }

            const toast = document.createElement('div');
            toast.className = 'grok-toast';
            if(type === 'error') toast.style.background = 'var(--grok-danger)';

            toast.innerHTML = `
                <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
                <span>${message}</span>
            `;
            container.appendChild(toast);

            setTimeout(() => {
                toast.style.opacity = '0';
                toast.style.transform = 'translateY(10px)';
                setTimeout(() => toast.remove(), 300);
            }, 3000);
        }

        function getCategories() {
            let cats = JSON.parse(GM_getValue('grok_categories', '["General"]'));
            if (!cats.includes('Image')) { cats.push('Image'); saveCategories(cats); }
            if (!cats.includes('Auto-History')) { cats.push('Auto-History'); saveCategories(cats); }
            return cats;
        }
        function saveCategories(cats) { GM_setValue('grok_categories', JSON.stringify(cats)); }
        function getPrompts() { return JSON.parse(GM_getValue('grok_prompts', '[]')); }
        function savePrompts(prompts) { GM_setValue('grok_prompts', JSON.stringify(prompts)); }
        function getSettings() { return JSON.parse(GM_getValue('grok_settings', '{"autoTrack": true}')); }
        function saveSettings(s) { GM_setValue('grok_settings', JSON.stringify(s)); }

        // --- IMAGE COMPRESSION ---
        function compressImage(file, callback) {
            const reader = new FileReader();
            reader.onload = (e) => {
                const img = new Image();
                img.onload = () => {
                    const canvas = document.createElement('canvas');
                    const ctx = canvas.getContext('2d');
                    const MAX_SIZE = 400;
                    let width = img.width;
                    let height = img.height;

                    if (width > height) {
                        if (width > MAX_SIZE) {
                            height *= MAX_SIZE / width;
                            width = MAX_SIZE;
                        }
                    } else {
                        if (height > MAX_SIZE) {
                            width *= MAX_SIZE / height;
                            height = MAX_SIZE;
                        }
                    }

                    canvas.width = width;
                    canvas.height = height;
                    ctx.drawImage(img, 0, 0, width, height);
                    callback(canvas.toDataURL('image/jpeg', 0.7));
                };
                img.src = e.target.result;
            };
            reader.readAsDataURL(file);
        }

        // --- AUTO TRACKING ---
        function setupAutoTracker() {
            const sendIconPath = "M5 11L12 4M12 4L19 11M12 4V21";

            const capture = () => {
                if (!getSettings().autoTrack) return;

                let text = '';
                let sourceType = 'Image';

                const editor = document.querySelector('.tiptap.ProseMirror');
                const imageInput = document.querySelector('textarea[aria-label="Image prompt"]');
                const videoInput = document.querySelector('textarea[aria-label="Make a video"]');

                if (videoInput && (document.activeElement === videoInput || (videoInput.value.trim().length > 0 && (!editor || !editor.textContent.trim())))) {
                    text = videoInput.value.trim();
                    sourceType = 'Video';
                }
                else if (imageInput && (document.activeElement === imageInput || (imageInput.value.trim().length > 0 && (!editor || !editor.textContent.trim())))) {
                    text = imageInput.value.trim();
                    sourceType = 'Image';
                }
                else if (editor && editor.textContent.trim().length > 0) {
                    text = editor.textContent.trim();
                    sourceType = 'Image';
                }

                if (!text && videoInput && videoInput.value.trim()) { text = videoInput.value.trim(); sourceType = 'Video'; }
                if (!text && imageInput && imageInput.value.trim()) { text = imageInput.value.trim(); sourceType = 'Image'; }

                if (!text || text.length < 2) return;

                const prompts = getPrompts();
                if (prompts.some(p => p.text === text)) return;

                prompts.push({
                    id: 'auto_' + Date.now().toString(),
                    text: text,
                    rating: 0,
                    category: 'Auto-History',
                    sourceType: sourceType,
                    timestamp: Date.now()
                });
                savePrompts(prompts);
                showToast(`Auto-Captured (${sourceType})`);

                if (isOpen && document.querySelector('.grok-prompt-tab.active[data-tab="recent"]')) {
                    renderPromptsList('grokRecentTab', null, (a,b) => b.timestamp - a.timestamp);
                }
            };

            document.addEventListener('keydown', (e) => {
                if (e.key === 'Enter' && !e.shiftKey) {
                    if (e.target.closest('.ProseMirror') || e.target.closest('textarea[aria-label="Image prompt"]') || e.target.closest('textarea[aria-label="Make a video"]')) {
                        capture();
                    }
                }
            }, true);

            document.addEventListener('mousedown', (e) => {
                const ariaBtn = e.target.closest('button[aria-label="Submit"]');
                const videoBtn = e.target.closest('button[aria-label="Make video"]');
                const btn = e.target.closest('button') || e.target.closest('[role="button"]') || e.target.closest('div');

                if (videoBtn) { capture(); return; }
                if (ariaBtn && ariaBtn.querySelector(`path[d="${sendIconPath}"]`)) { capture(); return; }
                if (btn && btn.querySelector(`path[d="${sendIconPath}"]`)) capture();
            }, true);
        }

        // --- UI CREATION ---
        function createUI() {
            document.querySelector('.grok-prompt-overlay')?.remove();
            document.querySelector('.grok-lightbox')?.remove();

            const lightbox = document.createElement('div');
            lightbox.className = 'grok-lightbox';
            lightbox.innerHTML = '<img src="" id="grokLightboxImg">';
            lightbox.onclick = () => { lightbox.classList.remove('open'); setTimeout(()=>lightbox.style.display='none', 200); };
            document.body.appendChild(lightbox);

            const overlay = document.createElement('div');
            overlay.className = 'grok-prompt-overlay';

            const modal = document.createElement('div');
            modal.className = 'grok-prompt-modal';
            modalElement = modal;

            const pos = GM_getValue('grok_modal_position', null);
            const size = GM_getValue('grok_modal_size', null);
            if (pos) { modal.style.top = pos.top; modal.style.left = pos.left; modal.style.transform = 'translate(0,0)'; }
            if (size) { modal.style.width = size.width; modal.style.height = size.height; }

            modal.innerHTML = `
                <div class="grok-prompt-header" id="grokDragHandle">
                    <div class="grok-prompt-title">
                        <svg width="24" height="24" fill="currentColor" viewBox="0 0 24 24" style="color: var(--grok-primary);">
                            <path d="M2 21L23 12L2 3V10L17 12L2 14V21Z"/>
                        </svg>
                        <span>Grok Imagine Manager</span>
                    </div>
                    <button class="grok-prompt-close" id="grokCloseBtn" title="Close (Esc)">
                        <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>
                    </button>
                </div>
                <div class="grok-prompt-tabs">
                    <button class="grok-prompt-tab active" data-tab="generate">New Prompt</button>
                    <button class="grok-prompt-tab" data-tab="recent">History</button>
                    <button class="grok-prompt-tab" data-tab="saved">Video</button>
                    <button class="grok-prompt-tab" data-tab="quick">Images</button>
                    <button class="grok-prompt-tab" data-tab="categories">Tags</button>
                    <button class="grok-prompt-tab" data-tab="settings">Settings</button>
                </div>
                <div class="grok-prompt-content">
                    <div id="grokGenerateTab" class="grok-prompt-form">
                        <div>
                            <label class="grok-prompt-label">Category</label>
                            <select class="grok-prompt-select" id="grokCategorySelect"></select>
                        </div>
                        <div>
                            <label class="grok-prompt-label">Your Prompt</label>
                            <textarea class="grok-prompt-textarea" id="grokPromptInput" placeholder="What do you want to see?"></textarea>
                        </div>
                        <div>
                            <label class="grok-prompt-label">Rating</label>
                            <div class="grok-prompt-stars" id="grokStars">
                                ${[1,2,3,4,5].map(i => `<svg class="grok-prompt-star" data-rating="${i}" fill="currentColor" viewBox="0 0 24 24"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>`).join('')}
                            </div>
                        </div>
                        <button class="grok-prompt-button" id="grokSaveBtn">
                            <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"/></svg>
                            Save Prompt
                        </button>
                    </div>

                    <div id="grokRecentTab" class="grok-prompt-list" style="display: none;"></div>
                    <div id="grokSavedTab" class="grok-prompt-list" style="display: none;"></div>
                    <div id="grokQuickTab" class="grok-prompt-list" style="display: none;"></div>

                    <div id="grokCategoriesTab" class="grok-prompt-form" style="display: none;">
                        <div>
                            <label class="grok-prompt-label">Add Category</label>
                            <div style="display:flex; gap:10px;">
                                <input type="text" class="grok-prompt-category-input" id="grokNewCategory" placeholder="e.g. Sci-Fi, Portraits...">
                                <button class="grok-prompt-add-btn" id="grokAddCategoryBtn">Add</button>
                            </div>
                            <div class="grok-prompt-category-list" id="grokCategoryList"></div>
                        </div>
                    </div>

                    <div id="grokSettingsTab" style="display: none;">
                        <label class="grok-prompt-label">Automation</label>
                        <div class="grok-checkbox-wrapper">
                            <input type="checkbox" id="grokAutoTrackToggle" class="grok-checkbox">
                            <div>
                                <div style="font-weight:600; color:var(--grok-text-main);">Auto-Capture Prompts</div>
                                <div style="font-size:12px; color:var(--grok-text-muted);">Automatically save prompts from Chat and Image Input.</div>
                            </div>
                        </div>
                        <label class="grok-prompt-label">Data Management</label>
                        <div style="display:flex; gap:12px; margin-bottom:24px;">
                            <button class="grok-prompt-export-btn" id="grokExportBtn">Export JSON</button>
                            <button class="grok-prompt-export-btn" id="grokImportBtn">Import JSON</button>
                            <input type="file" id="grokImportFile" style="display:none" accept=".json">
                        </div>
                        <div class="grok-prompt-export-section">
                            <label class="grok-prompt-label" style="color: var(--grok-danger);">Danger Zone</label>
                            <button class="grok-prompt-export-btn danger" id="grokClearBtn">Wipe All Data</button>
                        </div>
                    </div>
                </div>
                <div class="grok-prompt-resize-handle" id="grokResizeHandle"></div>
            `;

            overlay.appendChild(modal);
            document.body.appendChild(overlay);

            if (!GM_getValue('grok_hint_hidden', false)) {
                const hint = document.createElement('div');
                hint.className = 'grok-prompt-hint';
                hint.innerHTML = `<span>Press <span class="grok-prompt-kbd">Alt + K</span> to manage prompts</span>`;
                const closeHint = document.createElement('div');
                closeHint.innerHTML = '&times;';
                closeHint.style.cssText = 'cursor:pointer; font-size:16px; padding:0 5px;';
                closeHint.onclick = () => { hint.remove(); GM_setValue('grok_hint_hidden', true); };
                hint.appendChild(closeHint);
                document.body.appendChild(hint);
            }

            return overlay;
        }

        // --- RENDERERS ---
        function renderPromptsList(targetId, filterFn, sortFn) {
            const container = document.getElementById(targetId);
            let prompts = getPrompts();
            if (filterFn) prompts = prompts.filter(filterFn);

            // -- VIDEO TAB LOGIC (Includes Sort) --
            if (targetId === 'grokSavedTab') {
                const categories = getCategories().filter(cat => cat !== 'Image' && cat !== 'Auto-History');

                // Control Bar: Filters Left, Sort Right
                const controlsHTML = `
                    <div class="grok-control-bar">
                        <div class="grok-filter-group">
                            <button class="grok-prompt-filter-btn ${filterCategory === 'all' ? 'active' : ''}" data-f="all">All</button>
                            ${categories.map(c => `<button class="grok-prompt-filter-btn ${filterCategory === c ? 'active' : ''}" data-f="${c}">${c}</button>`).join('')}
                        </div>
                        <div class="grok-sort-group">
                             <span style="font-size:10px; color:#555; font-weight:700; text-transform:uppercase; margin-right:4px;">Sort:</span>
                             <button class="grok-prompt-sort-btn ${videoSortMode === 'newest' ? 'active' : ''}" data-sort="newest">Newest</button>
                             <button class="grok-prompt-sort-btn ${videoSortMode === 'high' ? 'active' : ''}" data-sort="high">High %</button>
                             <button class="grok-prompt-sort-btn ${videoSortMode === 'low' ? 'active' : ''}" data-sort="low">Low %</button>
                        </div>
                    </div>`;

                if (filterCategory !== 'all') prompts = prompts.filter(p => p.category === filterCategory);

                // Apply Video Sort Logic
                if (videoSortMode === 'high') {
                    prompts.sort((a, b) => (b.moderation || 0) - (a.moderation || 0));
                } else if (videoSortMode === 'low') {
                    prompts.sort((a, b) => (a.moderation || 0) - (b.moderation || 0));
                } else {
                    // Default: Newest
                    prompts.sort((a, b) => b.timestamp - a.timestamp);
                }

                if (prompts.length === 0) {
                    container.innerHTML = controlsHTML + `<div style="text-align:center; color:#666; padding:40px;">No saved prompts found.</div>`;
                    bindControls();
                    return;
                }
                container.innerHTML = controlsHTML + '<div class="grok-prompt-list">' + prompts.map(p => generatePromptHTML(p, 'video')).join('') + '</div>';
                bindControls();
                bindMediaEvents(container);
            }

            // -- IMAGE TAB --
            else if (targetId === 'grokQuickTab') {
                if (sortFn) prompts.sort(sortFn);
                 if (prompts.length === 0) {
                    container.innerHTML = `<div style="text-align:center; color:#666; padding:40px;">No image prompts saved yet.</div>`;
                    return;
                }
                container.innerHTML = '<div class="grok-prompt-list">' + prompts.map(p => generatePromptHTML(p, 'image')).join('') + '</div>';
                bindMediaEvents(container);
            }

            // -- HISTORY TAB --
            else {
                if (sortFn) prompts.sort(sortFn);
                if (prompts.length === 0) {
                    container.innerHTML = `<div style="text-align:center; color:#666; padding:40px;">History is empty.</div>`;
                    return;
                }
                container.innerHTML = '<div class="grok-prompt-list">' + prompts.map(p => generatePromptHTML(p, 'history')).join('') + '</div>';
                bindMediaEvents(container);
            }

            bindPromptEvents(container);
        }

        function generatePromptHTML(p, renderMode) {
            const isAuto = p.category === 'Auto-History' || p.id.startsWith('auto_');
            const videoPromptValue = p.videoPrompt || '';
            let badgeLabel = isAuto ? (p.sourceType || 'Auto-History') : p.category;

            let snapshotHtml = '';
            let videoInputHtml = '';

            if (renderMode === 'image') {
                snapshotHtml = p.snapshot
                    ? `<div class="grok-snapshot-wrapper"><img src="${p.snapshot}" class="grok-snapshot-thumb" data-src="${p.snapshot}" title="Click to Zoom"><button class="grok-snapshot-del" data-id="${p.id}" title="Remove Snapshot">&times;</button></div>`
                    : `<label class="grok-snapshot-upload-btn">+ Snapshot<input type="file" class="grok-snapshot-input" data-id="${p.id}" accept="image/jpeg, image/png, image/webp"></label>`;
            }

            if (renderMode === 'video') {
                 videoInputHtml = `
                 <div class="grok-video-section">
                    <label class="grok-video-label">Video Prompt</label>
                    <textarea class="grok-video-input" data-id="${p.id}" placeholder="Add video description...">${videoPromptValue}</textarea>
                    <div class="grok-video-save-hint">Press Tab to save</div>
                </div>`;
            }

            const previewCol = renderMode === 'image' ? `<div class="grok-image-preview-col">${snapshotHtml}</div>` : '';
            const modVal = p.moderation || 0;
            const modClass = modVal < 40 ? 'low' : (modVal < 80 ? 'med' : 'high');

            return `
            <div class="grok-prompt-item">
                <div class="grok-image-item-grid">
                    <div class="grok-image-content-col">
                        <div class="grok-prompt-item-header">
                            <div class="grok-prompt-item-text">${p.text}</div>
                            <button class="grok-prompt-item-delete" data-id="${p.id}" title="Delete"><svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg></button>
                        </div>
                        ${videoInputHtml}
                    </div>
                    ${previewCol}
                </div>
                <div class="grok-prompt-item-footer">
                    <span class="grok-prompt-category-badge ${isAuto ? 'auto' : ''}">${badgeLabel}</span>
                    <div class="grok-mod-wrapper">
                        <label class="grok-mod-label">Moderation Pass:</label>
                        <input type="range" min="0" max="100" value="${modVal}" class="grok-mod-slider" data-id="${p.id}">
                        <span class="grok-mod-val ${modClass}" id="mod-val-${p.id}">${modVal}%</span>
                    </div>
                    <button class="grok-prompt-copy-btn" data-text="${p.text.replace(/"/g, '&quot;')}">Copy</button>
                     ${isAuto ? `<button class="grok-prompt-copy-btn save-btn" data-id="${p.id}" style="color:var(--grok-primary); border-color:var(--grok-primary);">Save to Favorites</button>` : ''}
                     <span style="margin-left:auto; font-size:11px; color:#555;">${new Date(p.timestamp).toLocaleDateString()}</span>
                </div>
            </div>`;
        }

        function bindControls() {
            // Bind Filter Buttons
            document.querySelectorAll('.grok-prompt-filter-btn').forEach(b => {
                b.onclick = () => {
                    filterCategory = b.dataset.f;
                    renderPromptsList('grokSavedTab', p => p.category !== 'Image' && p.category !== 'Auto-History');
                };
            });

            // Bind Sort Buttons
            document.querySelectorAll('.grok-prompt-sort-btn').forEach(b => {
                b.onclick = () => {
                    videoSortMode = b.dataset.sort;
                    renderPromptsList('grokSavedTab', p => p.category !== 'Image' && p.category !== 'Auto-History');
                };
            });
        }

        function bindMediaEvents(container) {
             container.querySelectorAll('.grok-video-input').forEach(input => {
                 input.addEventListener('blur', (e) => {
                     const id = e.target.dataset.id;
                     const val = e.target.value;
                     let prompts = getPrompts();
                     let p = prompts.find(x => x.id === id);
                     if (p && p.videoPrompt !== val) {
                         p.videoPrompt = val;
                         savePrompts(prompts);
                         showToast('Video prompt saved');
                     }
                 });
             });

             container.querySelectorAll('.grok-mod-slider').forEach(slider => {
                 slider.addEventListener('input', (e) => {
                    const val = e.target.value;
                    const id = e.target.dataset.id;
                    const display = document.getElementById(`mod-val-${id}`);
                    if(display) {
                        display.innerText = val + '%';
                        display.className = `grok-mod-val ${val < 40 ? 'low' : (val < 80 ? 'med' : 'high')}`;
                    }
                 });
                 slider.addEventListener('change', (e) => {
                     const val = parseInt(e.target.value);
                     const id = e.target.dataset.id;
                     let prompts = getPrompts();
                     let p = prompts.find(x => x.id === id);
                     if (p) {
                         p.moderation = val;
                         savePrompts(prompts);
                         // If sorting by percentage, refresh list to reflect new order
                         if (videoSortMode !== 'newest') {
                             refreshActiveTab();
                         }
                     }
                 });
             });

             container.querySelectorAll('.grok-snapshot-input').forEach(input => {
                 input.addEventListener('change', (e) => {
                     const file = e.target.files[0];
                     if (!file) return;
                     const id = e.target.dataset.id;
                     compressImage(file, (base64) => {
                         let prompts = getPrompts();
                         let p = prompts.find(x => x.id === id);
                         if (p) {
                             p.snapshot = base64;
                             savePrompts(prompts);
                             refreshActiveTab();
                             showToast('Snapshot attached!');
                         }
                     });
                 });
             });

             container.querySelectorAll('.grok-snapshot-del').forEach(btn => {
                 btn.addEventListener('click', (e) => {
                     if (!confirm('Remove snapshot?')) return;
                     const id = e.target.dataset.id;
                     let prompts = getPrompts();
                     let p = prompts.find(x => x.id === id);
                     if (p) {
                         delete p.snapshot;
                         savePrompts(prompts);
                         refreshActiveTab();
                     }
                 });
             });

             container.querySelectorAll('.grok-snapshot-thumb').forEach(img => {
                 img.addEventListener('click', (e) => {
                     const lightbox = document.querySelector('.grok-lightbox');
                     const lbImg = document.getElementById('grokLightboxImg');
                     lbImg.src = e.target.dataset.src;
                     lightbox.style.display = 'flex';
                     lightbox.offsetHeight;
                     lightbox.classList.add('open');
                 });
             });
        }

        function bindPromptEvents(container) {
            container.onclick = (e) => {
                const btn = e.target.closest('button');
                if (!btn) return;

                if (btn.classList.contains('grok-prompt-copy-btn') && btn.dataset.text) {
                    navigator.clipboard.writeText(btn.dataset.text).then(() => showToast('Copied to clipboard!'));
                }
                else if (btn.classList.contains('grok-prompt-item-delete')) {
                    if (confirm('Delete this prompt?')) {
                        let prompts = getPrompts().filter(p => p.id !== btn.dataset.id);
                        savePrompts(prompts);
                        refreshActiveTab();
                    }
                }
                else if (btn.classList.contains('save-btn')) {
                    let prompts = getPrompts();
                    let p = prompts.find(x => x.id === btn.dataset.id);
                    if (p) {
                        document.querySelector('[data-tab="generate"]').click();
                        document.getElementById('grokPromptInput').value = p.text;
                        showToast('Edit and rate to save permanently', 'success');
                    }
                }
            };
        }

        function refreshActiveTab() {
            const active = document.querySelector('.grok-prompt-tab.active').dataset.tab;
            if (active === 'saved') renderPromptsList('grokSavedTab', p => p.category !== 'Image' && p.category !== 'Auto-History');
            else if (active === 'recent') renderPromptsList('grokRecentTab', null, (a,b) => b.timestamp - a.timestamp);
            else if (active === 'quick') renderPromptsList('grokQuickTab', p => p.category === 'Image', (a,b) => b.timestamp - a.timestamp);
            else if (active === 'categories') renderCategories();
            updateCounts();
        }

        function renderCategories() {
            const div = document.getElementById('grokCategoryList');
            div.innerHTML = getCategories().filter(c => c !== 'Auto-History').map(c => `
                <div class="grok-prompt-category-tag">
                    ${c}
                    <span style="cursor:pointer; color:#666; padding:0 4px;" data-rem="${c}">&times;</span>
                </div>
            `).join('');
            div.querySelectorAll('[data-rem]').forEach(span => {
                span.onclick = () => {
                    let c = span.dataset.rem;
                    let cats = getCategories();
                    if (cats.length <= 1) return alert('Keep at least one category.');
                    saveCategories(cats.filter(x => x !== c));
                    renderCategories();
                    updateCategorySelect();
                };
            });
        }

        function updateCategorySelect() {
            const sel = document.getElementById('grokCategorySelect');
            const cats = getCategories().filter(c => c !== 'Auto-History');
            sel.innerHTML = cats.map(c => `<option value="${c}">${c}</option>`).join('');
            if (!currentCategory) currentCategory = cats[0];
        }

        function updateCounts() {
            const p = getPrompts();
            document.querySelector('[data-tab="saved"]').innerText = `Video (${p.filter(x=>x.category!=='Image' && x.category!=='Auto-History').length})`;
            document.querySelector('[data-tab="quick"]').innerText = `Images (${p.filter(x=>x.category==='Image').length})`;
        }

        // --- INITIALIZATION ---
        const overlay = createUI();
        const modal = modalElement;
        setupAutoTracker();

        const handle = document.getElementById('grokDragHandle');
        handle.onmousedown = (e) => {
            if(e.target.closest('button')) return;
            isDragging = true;
            const rect = modal.getBoundingClientRect();
            dragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
            modal.style.transition = 'none';
            modal.style.transform = 'none';
            modal.style.left = rect.left + 'px';
            modal.style.top = rect.top + 'px';
        };

        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                modal.style.left = (e.clientX - dragOffset.x) + 'px';
                modal.style.top = (e.clientY - dragOffset.y) + 'px';
            }
            if (isResizing) {
                const rect = modal.getBoundingClientRect();
                modal.style.width = Math.max(400, e.clientX - rect.left) + 'px';
                modal.style.height = Math.max(300, e.clientY - rect.top) + 'px';
            }
        });

        document.addEventListener('mouseup', () => {
            if (isDragging || isResizing) {
                modal.style.transition = '';
                GM_setValue('grok_modal_position', { top: modal.style.top, left: modal.style.left });
                GM_setValue('grok_modal_size', { width: modal.style.width, height: modal.style.height });
            }
            isDragging = false; isResizing = false;
        });

        document.getElementById('grokResizeHandle').onmousedown = (e) => { e.preventDefault(); isResizing = true; };

        document.getElementById('grokCloseBtn').onclick = () => {
            overlay.classList.remove('open');
            setTimeout(() => overlay.style.display = 'none', 200);
            isOpen = false;
        };

        document.querySelectorAll('.grok-prompt-tab').forEach(tab => {
            tab.onclick = () => {
                document.querySelectorAll('.grok-prompt-tab').forEach(t => t.classList.remove('active'));
                tab.classList.add('active');
                const t = tab.dataset.tab;
                ['generate','recent','saved','quick','categories','settings'].forEach(id => {
                    document.getElementById('grok'+id.charAt(0).toUpperCase()+id.slice(1)+'Tab').style.display = (id === t ? (id==='categories' || id==='generate' || id==='settings' ? 'flex' : 'block') : 'none');
                });
                refreshActiveTab();
            };
        });

        const autoTrackToggle = document.getElementById('grokAutoTrackToggle');
        autoTrackToggle.checked = getSettings().autoTrack;
        autoTrackToggle.onchange = () => {
            saveSettings({ autoTrack: autoTrackToggle.checked });
            showToast(autoTrackToggle.checked ? 'Auto-Capture Enabled' : 'Auto-Capture Disabled');
        };

        document.querySelectorAll('.grok-prompt-star').forEach(s => {
            s.onclick = () => {
                currentRating = parseInt(s.dataset.rating);
                document.querySelectorAll('.grok-prompt-star').forEach((st,i) => {
                    st.classList.toggle('filled', i < currentRating);
                });
            };
        });

        document.getElementById('grokSaveBtn').onclick = () => {
            const text = document.getElementById('grokPromptInput').value.trim();
            if (!text || !currentRating) return alert('Please add text and a rating.');
            const cat = document.getElementById('grokCategorySelect').value;
            const prompts = getPrompts();
            prompts.push({ id: Date.now().toString(), text, rating: currentRating, category: cat, timestamp: Date.now() });
            savePrompts(prompts);
            document.getElementById('grokPromptInput').value = '';
            currentRating = 0;
            document.querySelectorAll('.grok-prompt-star').forEach(s => s.classList.remove('filled'));
            showToast('Prompt Saved!');
            document.querySelector('[data-tab="saved"]').click();
        };

        document.getElementById('grokAddCategoryBtn').onclick = () => {
            const val = document.getElementById('grokNewCategory').value.trim();
            if (!val) return;
            const cats = getCategories();
            if (!cats.includes(val)) {
                cats.push(val);
                saveCategories(cats);
                document.getElementById('grokNewCategory').value = '';
                renderCategories();
                updateCategorySelect();
                showToast('Category added');
            }
        };

        document.getElementById('grokExportBtn').onclick = () => {
            const data = JSON.stringify({ prompts: getPrompts(), categories: getCategories() }, null, 2);
            const blob = new Blob([data], {type: 'application/json'});
            const a = document.createElement('a');
            a.href = URL.createObjectURL(blob);
            a.download = `grok-prompts-${new Date().toISOString().split('T')[0]}.json`;
            a.click();
        };

        document.getElementById('grokImportBtn').onclick = () => document.getElementById('grokImportFile').click();
        document.getElementById('grokImportFile').onchange = (e) => {
            const fr = new FileReader();
            fr.onload = (ev) => {
                try {
                    const d = JSON.parse(ev.target.result);
                    if (confirm('Merge with existing (OK) or Replace All (Cancel)?')) {
                        savePrompts([...getPrompts(), ...d.prompts]);
                        saveCategories([...new Set([...getCategories(), ...d.categories])]);
                    } else {
                        savePrompts(d.prompts);
                        saveCategories(d.categories);
                    }
                    showToast('Import Successful');
                    refreshActiveTab();
                } catch(err) { alert('Invalid file'); }
            };
            fr.readAsText(e.target.files[0]);
        };

        document.getElementById('grokClearBtn').onclick = () => {
            if (confirm('PERMANENTLY DELETE ALL DATA?')) {
                savePrompts([]);
                saveCategories(['General']);
                location.reload();
            }
        };

        document.addEventListener('keydown', (e) => {
            if (e.altKey && e.key.toLowerCase() === 'k') {
                e.preventDefault();
                isOpen = !isOpen;
                if (isOpen) {
                    overlay.style.display = 'flex';
                    overlay.offsetHeight;
                    overlay.classList.add('open');
                    updateCounts();
                    updateCategorySelect();
                    refreshActiveTab();
                } else {
                    overlay.classList.remove('open');
                    setTimeout(() => overlay.style.display = 'none', 200);
                }
            }
            if (e.key === 'Escape' && isOpen) document.getElementById('grokCloseBtn').click();
        });

        updateCounts();
        updateCategorySelect();
    }

    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', initScript);
    else initScript();

})();