Grok Auto-Retry + Prompt Snippets + History (v24.0)

Separate video/image prompt tracking, increased history to 100 items, dual history tabs

目前為 2025-12-10 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Grok Auto-Retry + Prompt Snippets + History (v24.0)
// @namespace    http://tampermonkey.net/
// @version      24
// @description  Separate video/image prompt tracking, increased history to 100 items, dual history tabs
// @author       You
// @license      MIT
// @match        https://grok.com/*
// @match        https://*.grok.com/*
// @match        https://grok.x.ai/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';

    // --- CONFIGURATION ---
    const TARGET_TEXTAREA_SELECTOR = 'textarea[aria-label="Make a video"]';
    const IMAGE_EDITOR_SELECTOR = 'textarea[aria-label="Type to edit image..."]';
    const IMAGE_PROMPT_SELECTOR = 'textarea[aria-label="Image prompt"]';
    const IMAGE_IMAGINE_SELECTOR = 'p[data-placeholder="Type to imagine"]';
    const RETRY_BUTTON_SELECTOR = 'button[aria-label="Make video"]';
    const IMAGE_EDITOR_BUTTON_SELECTOR = 'button[aria-label="Generate"]';
    const IMAGE_SUBMIT_BUTTON_SELECTOR = 'button[aria-label="Submit"]';
    const MODERATION_TEXT = "Content Moderated. Try a different idea.";
    const RETRY_DELAY_MS = 1000;
    const COOLDOWN_MS = 2000;
    const OBSERVER_THROTTLE_MS = 500;
    const MAX_HISTORY_ITEMS = 100;

    // --- DEFAULT SNIPPETS ---
    const DEFAULT_SNIPPETS = [
        {
            id: 'b1', label: 'Anime Stickers (Provocative)',
            text: 'Surrounding the central image: thick decorative border made of overlapping colorful anime-style stickers featuring nude anime girls with exaggerated proportions in various provocative poses. Each sticker has a white outline and slight drop shadow. The stickers completely frame all four edges of the image with some overlap into the main content.'
        },
        {
            id: 'b2', label: 'Anime Stickers (SFW)',
            text: 'Surrounding the central image: thick decorative border made of overlapping colorful anime-style stickers featuring anime girls with exaggerated proportions in various poses. Each sticker has a white outline and slight drop shadow. The stickers completely frame all four edges of the image with some overlap into the main content.'
        },
        { id: '1', label: 'Motion: Slow Mo', text: 'slow motion, high frame rate, smooth movement' },
        { id: '2', label: 'Style: Photorealistic', text: 'photorealistic, 8k resolution, highly detailed, unreal engine 5 render' },
        { id: '3', label: 'Lighting: Golden Hour', text: 'golden hour lighting, warm sun rays, lens flare, soft shadows' },
    ];

    // --- LOAD SAVED SETTINGS ---
    let maxRetries = GM_getValue('maxRetries', 5);
    let uiToggleKey = GM_getValue('uiToggleKey', 'h');
    let autoClickEnabled = GM_getValue('autoClickEnabled', true);
    let isUiVisible = GM_getValue('isUiVisible', true);
    let savedSnippets = GM_getValue('savedSnippets', DEFAULT_SNIPPETS);
    let videoPromptHistory = GM_getValue('videoPromptHistory', []);
    let imagePromptHistory = GM_getValue('imagePromptHistory', []);
    let panelSize = GM_getValue('panelSize', { width: '300px', height: '400px' });

    let isRetryEnabled = true;
    let limitReached = false;
    let currentRetryCount = 0;
    let lastTypedPrompt = "";
    let lastRetryTimestamp = 0;
    let lastGenerationTimestamp = 0;
    const GENERATION_COOLDOWN_MS = 3000;

    let observerThrottle = false;
    let moderationDetected = false;
    let processingModeration = false;
    let currentHistoryTab = 'video'; // 'video' or 'image'

    // --- STYLES ---
    GM_addStyle(`
        #grok-control-panel {
            position: fixed; bottom: 20px; right: 20px;
            width: ${panelSize.width}; height: ${panelSize.height};
            min-width: 280px; min-height: 250px; max-width: 90vw; max-height: 90vh;
            background: linear-gradient(145deg, #0a0a0a 0%, #1a1a1a 100%);
            border: 1px solid #2a2a2a;
            border-radius: 16px;
            padding: 15px;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            color: #e0e0e0;
            z-index: 99998;
            box-shadow: 0 8px 32px rgba(0,0,0,0.9), 0 0 0 1px rgba(255,255,255,0.05);
            display: flex;
            flex-direction: column;
            gap: 10px;
        }
        #grok-control-panel.hidden { display: none; }
        #grok-resize-handle {
            position: absolute; top: 0; left: 0; width: 15px; height: 15px;
            cursor: nwse-resize; z-index: 99999;
        }
        #grok-resize-handle::after {
            content: ''; position: absolute; top: 2px; left: 2px;
            border-top: 6px solid #3b82f6; border-right: 6px solid transparent;
            width: 0; height: 0; opacity: 0.7;
        }
        #grok-resize-handle:hover::after { opacity: 1; border-top-color: #60a5fa; }
        .grok-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-shrink: 0;
            margin-left: 10px;
            padding-bottom: 8px;
            border-bottom: 1px solid #2a2a2a;
        }
        .grok-title {
            font-weight: bold;
            font-size: 14px;
            color: #f0f0f0;
            text-shadow: 0 2px 4px rgba(0,0,0,0.5);
        }
        .grok-toggle-btn {
            background: linear-gradient(135deg, #10b981 0%, #059669 100%);
            border: none;
            color: white;
            padding: 6px 14px;
            border-radius: 20px;
            font-size: 11px;
            font-weight: bold;
            cursor: pointer;
            box-shadow: 0 2px 8px rgba(16, 185, 129, 0.4);
            transition: all 0.2s ease;
        }
        .grok-toggle-btn:hover {
            background: linear-gradient(135deg, #059669 0%, #047857 100%);
            box-shadow: 0 4px 12px rgba(16, 185, 129, 0.6);
        }
        .grok-toggle-btn.off {
            background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
            box-shadow: 0 2px 8px rgba(239, 68, 68, 0.4);
        }
        .grok-toggle-btn.off:hover {
            background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
            box-shadow: 0 4px 12px rgba(239, 68, 68, 0.6);
        }
        .grok-controls {
            display: flex;
            align-items: center;
            justify-content: space-between;
            font-size: 12px;
            color: #9ca3af;
            flex-shrink: 0;
            padding: 8px 0;
        }
        .grok-checkbox {
            display: flex;
            align-items: center;
            cursor: pointer;
            color: #d1d5db;
        }
        .grok-checkbox input {
            margin-right: 6px;
            cursor: pointer;
            accent-color: #3b82f6;
        }
        .grok-num-input {
            width: 40px;
            background: #1f1f1f;
            border: 1px solid #2a2a2a;
            color: #e0e0e0;
            border-radius: 6px;
            padding: 4px 6px;
            text-align: center;
            transition: all 0.2s ease;
        }
        .grok-num-input:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
        }
        .grok-prompt-label {
            font-size: 11px;
            font-weight: bold;
            color: #9ca3af;
            margin-bottom: -5px;
            flex-shrink: 0;
        }
        #grok-panel-prompt {
            width: 100%;
            flex-grow: 1;
            background: #0f0f0f;
            border: 1px solid #2a2a2a;
            border-radius: 8px;
            color: #e0e0e0;
            padding: 10px;
            font-size: 12px;
            font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
            resize: none;
            box-sizing: border-box;
            transition: all 0.2s ease;
        }
        #grok-panel-prompt:focus {
            border-color: #3b82f6;
            outline: none;
            box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
        }
        #grok-panel-prompt::placeholder {
            color: #4b5563;
        }
        .grok-btn-row {
            display: flex;
            gap: 8px;
            flex-shrink: 0;
        }
        .grok-action-btn {
            flex: 1;
            padding: 10px;
            border-radius: 8px;
            border: none;
            cursor: pointer;
            font-weight: 600;
            font-size: 12px;
            transition: all 0.2s ease;
            position: relative;
            overflow: hidden;
        }
        .grok-action-btn::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);
            transition: left 0.5s;
        }
        .grok-action-btn:hover::before {
            left: 100%;
        }
        #btn-open-library {
            background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
            color: white;
            box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
        }
        #btn-open-library:hover {
            background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
            box-shadow: 0 4px 12px rgba(59, 130, 246, 0.5);
            transform: translateY(-1px);
        }
        #btn-open-history {
            background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
            color: white;
            box-shadow: 0 2px 8px rgba(139, 92, 246, 0.3);
        }
        #btn-open-history:hover {
            background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);
            box-shadow: 0 4px 12px rgba(139, 92, 246, 0.5);
            transform: translateY(-1px);
        }
        #btn-generate {
            background: linear-gradient(135deg, #1f1f1f 0%, #2a2a2a 100%);
            color: #e0e0e0;
            border: 1px solid #3a3a3a;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
        }
        #btn-generate:hover {
            background: linear-gradient(135deg, #2a2a2a 0%, #353535 100%);
            border-color: #4a4a4a;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
            transform: translateY(-1px);
        }
        #grok-status {
            text-align: center;
            font-size: 11px;
            color: #10b981;
            padding-top: 8px;
            border-top: 1px solid #2a2a2a;
            flex-shrink: 0;
            font-weight: 500;
        }
        .status-error {
            color: #ef4444 !important;
        }

        /* Modal Styles */
        .grok-modal {
            position: fixed;
            right: 20px;
            width: 350px;
            height: 400px;
            background: linear-gradient(145deg, #0a0a0a 0%, #1a1a1a 100%);
            border: 1px solid #2a2a2a;
            border-radius: 16px;
            display: none;
            flex-direction: column;
            z-index: 99999;
            box-shadow: 0 8px 32px rgba(0,0,0,0.9), 0 0 0 1px rgba(255,255,255,0.05);
            font-family: -apple-system, BlinkMacSystemFont, sans-serif;
        }
        .grok-modal.active { display: flex; }
        .gl-header {
            padding: 12px 15px;
            background: linear-gradient(135deg, #1a1a1a 0%, #0f0f0f 100%);
            display: flex;
            justify-content: space-between;
            align-items: center;
            font-weight: bold;
            font-size: 13px;
            color: #f0f0f0;
            border-bottom: 1px solid #2a2a2a;
            border-radius: 16px 16px 0 0;
        }
        .gl-close {
            cursor: pointer;
            font-size: 20px;
            line-height: 1;
            color: #6b7280;
            transition: all 0.2s ease;
            width: 24px;
            height: 24px;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 6px;
        }
        .gl-close:hover {
            color: #f0f0f0;
            background: #2a2a2a;
        }

        /* History Tab Styles */
        .history-tabs {
            display: flex;
            background: #0f0f0f;
            border-bottom: 1px solid #2a2a2a;
        }
        .history-tab {
            flex: 1;
            padding: 10px;
            text-align: center;
            cursor: pointer;
            font-size: 11px;
            font-weight: 600;
            color: #6b7280;
            transition: all 0.2s ease;
            border-bottom: 2px solid transparent;
        }
        .history-tab:hover {
            color: #9ca3af;
            background: #1a1a1a;
        }
        .history-tab.active {
            color: #8b5cf6;
            border-bottom-color: #8b5cf6;
            background: #1a1a1a;
        }

        .gl-view-list {
            display: flex;
            flex-direction: column;
            height: 100%;
            overflow: hidden;
        }
        .gl-list-content {
            overflow-y: auto;
            padding: 12px;
            flex: 1;
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        .gl-list-content::-webkit-scrollbar {
            width: 8px;
        }
        .gl-list-content::-webkit-scrollbar-track {
            background: #0f0f0f;
        }
        .gl-list-content::-webkit-scrollbar-thumb {
            background: #2a2a2a;
            border-radius: 4px;
        }
        .gl-list-content::-webkit-scrollbar-thumb:hover {
            background: #3a3a3a;
        }
        .gl-item {
            background: linear-gradient(135deg, #1a1a1a 0%, #151515 100%);
            border: 1px solid #2a2a2a;
            padding: 10px;
            border-radius: 8px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            font-size: 12px;
            color: #e0e0e0;
            transition: all 0.2s ease;
        }
        .gl-item:hover {
            border-color: #3b82f6;
            background: linear-gradient(135deg, #1f1f1f 0%, #1a1a1a 100%);
            box-shadow: 0 2px 8px rgba(59, 130, 246, 0.2);
            transform: translateY(-1px);
        }
        .gl-item-text {
            cursor: pointer;
            flex: 1;
            margin-right: 10px;
        }
        .gl-item-text b {
            display: block;
            margin-bottom: 4px;
            color: #f0f0f0;
        }
        .gl-item-text span {
            color: #9ca3af;
            font-size: 10px;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
            overflow: hidden;
            line-height: 1.4;
        }
        .gl-item-actions {
            display: flex;
            gap: 6px;
        }
        .gl-icon-btn {
            background: #1f1f1f;
            border: 1px solid #2a2a2a;
            cursor: pointer;
            font-size: 14px;
            color: #9ca3af;
            padding: 6px;
            border-radius: 6px;
            transition: all 0.2s ease;
            width: 28px;
            height: 28px;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .gl-icon-btn:hover {
            color: #f0f0f0;
            background: #2a2a2a;
            border-color: #3a3a3a;
            transform: scale(1.1);
        }
        .gl-create-btn {
            margin: 12px;
            padding: 10px;
            background: linear-gradient(135deg, #10b981 0%, #059669 100%);
            color: white;
            text-align: center;
            border-radius: 8px;
            cursor: pointer;
            font-weight: 600;
            font-size: 12px;
            box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
            transition: all 0.2s ease;
        }
        .gl-create-btn:hover {
            background: linear-gradient(135deg, #059669 0%, #047857 100%);
            box-shadow: 0 4px 12px rgba(16, 185, 129, 0.5);
            transform: translateY(-1px);
        }
        .gl-view-editor {
            display: none;
            flex-direction: column;
            padding: 15px;
            height: 100%;
            gap: 10px;
        }
        .gl-view-editor.active { display: flex; }
        .gl-input, .gl-textarea {
            background: #0f0f0f;
            border: 1px solid #2a2a2a;
            color: #e0e0e0;
            padding: 10px;
            border-radius: 8px;
            font-size: 12px;
            width: 100%;
            box-sizing: border-box;
            transition: all 0.2s ease;
        }
        .gl-input:focus, .gl-textarea:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
        }
        .gl-textarea {
            flex-grow: 1;
            resize: none;
            font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
        }
        .gl-editor-buttons {
            display: flex;
            gap: 10px;
            margin-top: auto;
        }
        .gl-btn {
            flex: 1;
            padding: 10px;
            border-radius: 8px;
            border: none;
            cursor: pointer;
            font-weight: 600;
            color: white;
            font-size: 12px;
            transition: all 0.2s ease;
        }
        .gl-btn-save {
            background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
            box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
        }
        .gl-btn-save:hover {
            background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
            box-shadow: 0 4px 12px rgba(59, 130, 246, 0.5);
            transform: translateY(-1px);
        }
        .gl-btn-cancel {
            background: linear-gradient(135deg, #374151 0%, #1f2937 100%);
            box-shadow: 0 2px 8px rgba(55, 65, 81, 0.3);
        }
        .gl-btn-cancel:hover {
            background: linear-gradient(135deg, #4b5563 0%, #374151 100%);
            box-shadow: 0 4px 12px rgba(55, 65, 81, 0.5);
            transform: translateY(-1px);
        }

        /* History Specific Styles */
        .history-item-time {
            font-size: 9px;
            color: #6b7280;
            margin-top: 3px;
        }
        .history-clear-btn {
            margin: 12px;
            padding: 10px;
            background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
            color: white;
            text-align: center;
            border-radius: 8px;
            cursor: pointer;
            font-weight: 600;
            font-size: 12px;
            box-shadow: 0 2px 8px rgba(239, 68, 68, 0.3);
            transition: all 0.2s ease;
        }
        .history-clear-btn:hover {
            background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
            box-shadow: 0 4px 12px rgba(239, 68, 68, 0.5);
            transform: translateY(-1px);
        }
    `);

    // --- DOM CREATION ---
    const panel = document.createElement('div');
    panel.id = 'grok-control-panel';
    if (!isUiVisible) panel.classList.add('hidden');
    panel.innerHTML = `
        <div id="grok-resize-handle" title="Drag to Resize"></div>
        <div class="grok-header">
            <span class="grok-title">Grok Tools v18.0</span>
            <button id="grok-toggle-btn" class="grok-toggle-btn">ON</button>
        </div>
        <div class="grok-controls">
            <label class="grok-checkbox">
                <input type="checkbox" id="grok-autoclick-cb" ${autoClickEnabled ? 'checked' : ''}> Auto-Retry
            </label>
            <div>
                Max: <input type="number" id="grok-retry-limit" value="${maxRetries}" class="grok-num-input" min="1">
            </div>
        </div>
        <div class="grok-prompt-label">Prompt Editor</div>
        <textarea id="grok-panel-prompt" placeholder="Type or paste prompt here..."></textarea>
        <div class="grok-btn-row">
            <button id="btn-open-library" class="grok-action-btn">Snippets</button>
            <button id="btn-open-history" class="grok-action-btn">History</button>
            <button id="btn-generate" class="grok-action-btn">Generate</button>
        </div>
        <div id="grok-status">Ready</div>
        <div style="font-size:9px; color:#555; text-align:center;">Hide: Alt+${uiToggleKey.toUpperCase()}</div>
    `;
    document.body.appendChild(panel);

    // --- LIBRARY MODAL ---
    const modal = document.createElement('div');
    modal.id = 'grok-library-modal';
    modal.className = 'grok-modal';
    modal.innerHTML = `
        <div class="gl-header"><span>Snippets Library</span><span class="gl-close">&times;</span></div>
        <div class="gl-view-list" id="gl-view-list">
            <div class="gl-list-content" id="gl-list-container"></div>
            <div class="gl-create-btn" id="btn-create-snippet">Create New Snippet</div>
        </div>
        <div class="gl-view-editor" id="gl-view-editor">
            <label style="font-size:11px; color:#8b98a5;">Label</label>
            <input type="text" class="gl-input" id="gl-edit-label" placeholder="e.g. Cinematic Lighting">
            <label style="font-size:11px; color:#8b98a5;">Prompt Text</label>
            <textarea class="gl-textarea" id="gl-edit-text" placeholder="Content to append..."></textarea>
            <div class="gl-editor-buttons">
                <button class="gl-btn gl-btn-cancel" id="btn-edit-cancel">Cancel</button>
                <button class="gl-btn gl-btn-save" id="btn-edit-save">Save Snippet</button>
            </div>
        </div>
    `;
    document.body.appendChild(modal);

    // --- HISTORY MODAL ---
    const historyModal = document.createElement('div');
    historyModal.id = 'grok-history-modal';
    historyModal.className = 'grok-modal';
    historyModal.innerHTML = `
        <div class="gl-header"><span>Prompt History (100 max)</span><span class="gl-close history-close">&times;</span></div>
        <div class="history-tabs">
            <div class="history-tab active" data-tab="video">🎥 Video</div>
            <div class="history-tab" data-tab="image">🖼️ Image</div>
        </div>
        <div class="gl-view-list" id="history-view-list">
            <div class="gl-list-content" id="history-list-container"></div>
            <div class="history-clear-btn" id="btn-clear-history">Clear This Tab's History</div>
        </div>
        <div class="gl-view-editor history-viewer" id="history-view-viewer">
            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px;">
                <label style="font-size:11px; color:#9ca3af; font-weight: 600;">Full Prompt</label>
                <span id="history-viewer-time" style="font-size:9px; color:#6b7280;"></span>
            </div>
            <textarea class="gl-textarea" id="history-viewer-text" readonly></textarea>
            <div class="gl-editor-buttons">
                <button class="gl-btn gl-btn-cancel" id="btn-viewer-back">← Back</button>
                <button class="gl-btn gl-btn-save" id="btn-viewer-use">Use This Prompt</button>
            </div>
        </div>
    `;
    document.body.appendChild(historyModal);

    // --- RESIZE LOGIC ---
    const resizeHandle = document.getElementById('grok-resize-handle');
    let isResizing = false;
    let startX, startY, startWidth, startHeight;

    function updateModalPosition() {
        const pHeight = panel.offsetHeight;
        const bottom = (20 + pHeight + 10) + 'px';
        modal.style.bottom = bottom;
        historyModal.style.bottom = bottom;
    }

    resizeHandle.addEventListener('mousedown', (e) => {
        isResizing = true;
        startX = e.clientX;
        startY = e.clientY;
        const rect = panel.getBoundingClientRect();
        startWidth = rect.width;
        startHeight = rect.height;
        e.preventDefault();
        document.body.style.cursor = 'nwse-resize';
    });

    document.addEventListener('mousemove', (e) => {
        if (!isResizing) return;
        const deltaX = startX - e.clientX;
        const deltaY = startY - e.clientY;
        const newWidth = Math.max(280, startWidth + deltaX);
        const newHeight = Math.max(250, startHeight + deltaY);
        panel.style.width = newWidth + 'px';
        panel.style.height = newHeight + 'px';
        updateModalPosition();
    });

    document.addEventListener('mouseup', () => {
        if (isResizing) {
            isResizing = false;
            document.body.style.cursor = '';
            const rect = panel.getBoundingClientRect();
            GM_setValue('panelSize', { width: rect.width + 'px', height: rect.height + 'px' });
            updateModalPosition();
        }
    });

    // --- REFS ---
    const toggleBtn = document.getElementById('grok-toggle-btn');
    const autoClickCb = document.getElementById('grok-autoclick-cb');
    const limitInput = document.getElementById('grok-retry-limit');
    const statusText = document.getElementById('grok-status');
    const promptBox = document.getElementById('grok-panel-prompt');
    const openLibBtn = document.getElementById('btn-open-library');
    const openHistoryBtn = document.getElementById('btn-open-history');
    const generateBtn = document.getElementById('btn-generate');
    const modalEl = document.getElementById('grok-library-modal');
    const historyModalEl = document.getElementById('grok-history-modal');
    const listContainer = document.getElementById('gl-list-container');
    const historyListContainer = document.getElementById('history-list-container');
    const createBtn = document.getElementById('btn-create-snippet');
    const clearHistoryBtn = document.getElementById('btn-clear-history');
    const editLabel = document.getElementById('gl-edit-label');
    const editText = document.getElementById('gl-edit-text');
    let editingId = null;

    // --- HELPER FUNCTIONS ---
    function nativeValueSet(el, value) {
        const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
        setter.call(el, value);
        el.dispatchEvent(new Event('input', { bubbles: true }));
    }

    function resetState(msg) {
        limitReached = false;
        currentRetryCount = 0;
        moderationDetected = false;
        processingModeration = false;
        updateStatus(msg);
    }

    function updateStatus(msg, type) {
        statusText.textContent = msg;
        statusText.className = type === 'error' ? 'status-error' : '';
    }

    function addToHistory(prompt, type) {
        if (!prompt || !prompt.trim()) return;

        const historyArray = type === 'image' ? imagePromptHistory : videoPromptHistory;

        // Remove duplicates
        const filtered = historyArray.filter(item => item.text !== prompt);

        // Add to beginning
        filtered.unshift({
            id: Date.now().toString(),
            text: prompt,
            timestamp: Date.now(),
            type: type
        });

        // Limit history size
        const limited = filtered.slice(0, MAX_HISTORY_ITEMS);

        if (type === 'image') {
            imagePromptHistory = limited;
            GM_setValue('imagePromptHistory', imagePromptHistory);
        } else {
            videoPromptHistory = limited;
            GM_setValue('videoPromptHistory', videoPromptHistory);
        }
    }

    function formatTimestamp(timestamp) {
        const date = new Date(timestamp);
        const now = new Date();
        const diff = now - date;

        if (diff < 60000) return 'Just now';
        if (diff < 3600000) {
            const mins = Math.floor(diff / 60000);
            return `${mins} minute${mins > 1 ? 's' : ''} ago`;
        }
        if (diff < 86400000) {
            const hours = Math.floor(diff / 3600000);
            return `${hours} hour${hours > 1 ? 's' : ''} ago`;
        }
        return date.toLocaleDateString();
    }

    function detectPromptType() {
        // Check for video generation button or video textarea
        const videoBtn = document.querySelector(RETRY_BUTTON_SELECTOR);
        const videoTA = document.querySelector(TARGET_TEXTAREA_SELECTOR);

        // If video button exists and is visible/enabled, it's video mode
        if (videoBtn && !videoBtn.disabled) {
            return 'video';
        }

        // If video textarea exists and has focus or content, it's video
        if (videoTA && (document.activeElement === videoTA || videoTA.value.trim())) {
            return 'video';
        }

        // Check if we're on image prompt interface
        if (document.querySelector(IMAGE_PROMPT_SELECTOR) ||
            document.querySelector(IMAGE_IMAGINE_SELECTOR) ||
            document.querySelector(IMAGE_EDITOR_SELECTOR)) {
            return 'image';
        }

        return 'video'; // default to video
    }

    // --- PROMPT SYNC: Panel -> Grok ---
    let syncTimeout = null;
    let isUserTyping = false;

    promptBox.addEventListener('input', () => {
        clearTimeout(syncTimeout);
        syncTimeout = setTimeout(() => {
            const grokTA = document.querySelector(TARGET_TEXTAREA_SELECTOR) ||
                           document.querySelector(IMAGE_EDITOR_SELECTOR) ||
                           document.querySelector(IMAGE_PROMPT_SELECTOR);

            const imagineP = document.querySelector(IMAGE_IMAGINE_SELECTOR);

            // Only sync if user is NOT actively typing in the Grok inputs
            if (!isUserTyping) {
                if (grokTA && document.activeElement !== grokTA) {
                    lastTypedPrompt = promptBox.value;
                    nativeValueSet(grokTA, lastTypedPrompt);
                    resetState("Ready");
                } else if (imagineP && document.activeElement !== imagineP) {
                    lastTypedPrompt = promptBox.value;
                    // Don't modify contenteditable while it might be in use
                    resetState("Ready");
                }
            }
        }, 100);
    });

    // --- PROMPT SYNC: Grok -> Panel ---
    let lastSyncedValue = '';
    let typingTimer = null;

    // Handle textarea inputs (video + image textarea)
    document.addEventListener('input', (e) => {
        if (e.target.matches(TARGET_TEXTAREA_SELECTOR) ||
            e.target.matches(IMAGE_EDITOR_SELECTOR) ||
            e.target.matches(IMAGE_PROMPT_SELECTOR)) {
            isUserTyping = true;
            clearTimeout(typingTimer);

            const val = e.target.value;
            if (val !== lastSyncedValue) {
                lastSyncedValue = val;
                promptBox.value = val;
                lastTypedPrompt = val;
                if (val.trim()) resetState("Ready");
            }

            typingTimer = setTimeout(() => {
                isUserTyping = false;
            }, 500);
        }
    }, { capture: true, passive: true });

    // Handle contenteditable inputs (.tiptap.ProseMirror and imagine paragraph)
    document.addEventListener('keydown', (e) => {
        // Check for .tiptap.ProseMirror element (main image input)
        const editor = document.querySelector('.tiptap.ProseMirror');
        const imagineP = document.querySelector(IMAGE_IMAGINE_SELECTOR);

        if ((editor && e.target.closest('.tiptap.ProseMirror')) ||
            (imagineP && document.activeElement === imagineP)) {
            isUserTyping = true;
            clearTimeout(typingTimer);
            typingTimer = setTimeout(() => {
                isUserTyping = false;

                // Capture text after typing stops
                let capturedText = '';
                if (editor && editor.textContent.trim()) {
                    capturedText = editor.textContent.trim();
                } else if (imagineP && (imagineP.textContent || imagineP.innerText || '').trim()) {
                    capturedText = (imagineP.textContent || imagineP.innerText || '').trim();
                }

                if (capturedText && capturedText !== promptBox.value) {
                    promptBox.value = capturedText;
                    lastTypedPrompt = capturedText;
                    resetState("Ready");
                }
            }, 500);
        }
    }, { capture: true, passive: true });

    // Watch for changes to imagine paragraph - ONLY capture, don't modify
    let lastImagineValue = '';
    const imagineObserver = new MutationObserver((mutations) => {
        if (isUserTyping) {
            const editor = document.querySelector('.tiptap.ProseMirror');
            const imagineP = document.querySelector(IMAGE_IMAGINE_SELECTOR);

            let val = '';
            if (editor && editor.textContent.trim()) {
                val = editor.textContent.trim();
            } else if (imagineP) {
                val = (imagineP.textContent || imagineP.innerText || '').trim();
            }

            if (val && val !== lastImagineValue && val !== promptBox.value) {
                lastImagineValue = val;
                promptBox.value = val;
                lastTypedPrompt = val;

                // Remove is-empty class if there's content
                if (imagineP && imagineP.classList.contains('is-empty') && val) {
                    imagineP.classList.remove('is-empty', 'is-editor-empty');
                }
            }
        }
    });

    // Start observing imagine element if it exists
    const startImagineObserver = () => {
        const editor = document.querySelector('.tiptap.ProseMirror');
        const imagineP = document.querySelector(IMAGE_IMAGINE_SELECTOR);

        if (editor) {
            imagineObserver.observe(editor, {
                characterData: true,
                childList: true,
                subtree: true,
                attributes: false
            });
        }

        if (imagineP) {
            imagineObserver.observe(imagineP, {
                characterData: true,
                childList: true,
                subtree: true,
                attributes: false
            });
        }
    };
    startImagineObserver();

    // Also listen for input events on the contenteditable (.tiptap.ProseMirror)
    document.addEventListener('input', (e) => {
        const target = e.target;
        if (target && target.closest && target.closest('.tiptap.ProseMirror')) {
            isUserTyping = true;
            clearTimeout(typingTimer);

            const editor = document.querySelector('.tiptap.ProseMirror');
            const val = editor ? (editor.textContent || editor.innerText || '').trim() : '';
            if (val && val !== lastImagineValue) {
                lastImagineValue = val;
                promptBox.value = val;
                lastTypedPrompt = val;
                resetState("Ready");
            }

            typingTimer = setTimeout(() => {
                isUserTyping = false;
            }, 500);
        }
    }, true);

    // --- MANUAL GENERATE ---
    generateBtn.addEventListener('click', () => {
        const grokTA = document.querySelector(TARGET_TEXTAREA_SELECTOR) ||
                       document.querySelector(IMAGE_EDITOR_SELECTOR) ||
                       document.querySelector(IMAGE_PROMPT_SELECTOR);
        const realBtn = document.querySelector(RETRY_BUTTON_SELECTOR) ||
                        document.querySelector(IMAGE_EDITOR_BUTTON_SELECTOR) ||
                        document.querySelector(IMAGE_SUBMIT_BUTTON_SELECTOR);
        const imagineP = document.querySelector(IMAGE_IMAGINE_SELECTOR);

        if (!grokTA && !imagineP) {
            updateStatus("Grok elements not found!", "error");
            return;
        }
        if (!realBtn) {
            updateStatus("Generate button not found!", "error");
            return;
        }

        const now = Date.now();
        if (now - lastGenerationTimestamp < GENERATION_COOLDOWN_MS) {
            const remaining = Math.ceil((GENERATION_COOLDOWN_MS - (now - lastGenerationTimestamp)) / 1000);
            updateStatus(`Cooldown: ${remaining}s remaining`, "error");
            return;
        }

        const promptToGenerate = promptBox.value.trim();
        if (promptToGenerate) {
            // Determine type based on which button we're clicking
            let promptType = 'image'; // default

            if (realBtn.matches(RETRY_BUTTON_SELECTOR)) {
                // It's the video "Make video" button
                promptType = 'video';
            } else if (realBtn.matches(IMAGE_SUBMIT_BUTTON_SELECTOR) ||
                       realBtn.matches(IMAGE_EDITOR_BUTTON_SELECTOR)) {
                // It's an image button
                promptType = 'image';
            } else {
                // Fallback to detection
                promptType = detectPromptType();
            }

            addToHistory(promptToGenerate, promptType);
        }

        if (grokTA) {
            nativeValueSet(grokTA, promptBox.value);
        } else if (imagineP) {
            imagineP.textContent = promptBox.value;
            if (imagineP.classList.contains('is-empty') && promptBox.value) {
                imagineP.classList.remove('is-empty');
            }
        }

        setTimeout(() => {
            if (!realBtn.disabled) {
                realBtn.click();
                lastGenerationTimestamp = Date.now();
                updateStatus("Generation Started...");
            } else {
                updateStatus("Grok button disabled/processing.", "error");
            }
        }, 50);
    });

    // --- CAPTURE IMAGE SUBMIT CLICKS ---
    let isAutoRetryClick = false; // Prevent double-counting during retries

    // Capture Submit button clicks
    document.addEventListener('mousedown', (e) => {
        if (isAutoRetryClick) return;

        const submitBtn = e.target.closest('button[aria-label="Submit"]');
        if (submitBtn) {
            // Capture immediately on mousedown (before the click processes)
            const editor = document.querySelector('.tiptap.ProseMirror');
            const imagineP = document.querySelector(IMAGE_IMAGINE_SELECTOR);
            const imageTA = document.querySelector(IMAGE_PROMPT_SELECTOR);

            let promptToCapture = '';

            // Priority order for capturing (matching main script logic)
            if (editor && editor.textContent.trim().length > 0) {
                promptToCapture = editor.textContent.trim();
            } else if (imagineP) {
                const imagineText = (imagineP.textContent || imagineP.innerText || '').trim();
                if (imagineText) {
                    promptToCapture = imagineText;
                }
            } else if (imageTA && imageTA.value.trim()) {
                promptToCapture = imageTA.value.trim();
            } else if (promptBox.value.trim()) {
                promptToCapture = promptBox.value.trim();
            } else if (lastTypedPrompt.trim()) {
                promptToCapture = lastTypedPrompt.trim();
            }

            if (promptToCapture && promptToCapture.length > 2) {
                addToHistory(promptToCapture, 'image');
                updateStatus("Image prompt captured!");
                setTimeout(() => {
                    if (isRetryEnabled) {
                        updateStatus("Ready");
                    }
                }, 2000);
            }
        }
    }, true);

    // Capture Enter key press in image inputs
    document.addEventListener('keydown', (e) => {
        if (e.key === 'Enter' && !e.shiftKey) {
            // EXCLUDE video textarea - don't capture it as image
            if (e.target.matches('textarea[aria-label="Make a video"]')) {
                return; // Let video capture handle this
            }

            // Check if typing in image prompt textarea
            if (e.target.matches('textarea[aria-label="Image prompt"]')) {
                const val = e.target.value.trim();
                if (val && val.length > 2) {
                    addToHistory(val, 'image');
                    updateStatus("Image prompt captured!");
                    setTimeout(() => {
                        if (isRetryEnabled) {
                            updateStatus("Ready");
                        }
                    }, 2000);
                }
            }
            // Check if typing in .tiptap.ProseMirror or imagine paragraph
            else if (e.target.closest('.tiptap.ProseMirror')) {
                const editor = e.target.closest('.tiptap.ProseMirror');
                if (editor && editor.textContent.trim().length > 2) {
                    const val = editor.textContent.trim();
                    addToHistory(val, 'image');
                    updateStatus("Image prompt captured!");
                    setTimeout(() => {
                        if (isRetryEnabled) {
                            updateStatus("Ready");
                        }
                    }, 2000);
                }
            }
            else if (e.target.matches(IMAGE_IMAGINE_SELECTOR)) {
                const imagineP = e.target;
                const val = (imagineP.textContent || imagineP.innerText || '').trim();
                if (val && val.length > 2) {
                    addToHistory(val, 'image');
                    updateStatus("Image prompt captured!");
                    setTimeout(() => {
                        if (isRetryEnabled) {
                            updateStatus("Ready");
                        }
                    }, 2000);
                }
            }
        }
    }, true);

    // --- HISTORY TAB SWITCHING ---
    const historyTabs = historyModal.querySelectorAll('.history-tab');
    historyTabs.forEach(tab => {
        tab.addEventListener('click', () => {
            historyTabs.forEach(t => t.classList.remove('active'));
            tab.classList.add('active');
            currentHistoryTab = tab.dataset.tab;
            renderHistory();
        });
    });

    // --- HISTORY MODAL LOGIC ---
    function renderHistory() {
        historyListContainer.innerHTML = '';

        const historyArray = currentHistoryTab === 'image' ? imagePromptHistory : videoPromptHistory;

        if (historyArray.length === 0) {
            historyListContainer.innerHTML = `<div style="text-align:center; padding:20px; color:#555;">No ${currentHistoryTab} history yet</div>`;
            return;
        }

        historyArray.forEach(item => {
            const el = document.createElement('div');
            el.className = 'gl-item history-item';
            el.innerHTML = `
                <div class="gl-item-text history-text">
                    <span class="history-preview">${escapeHtml(item.text)}</span>
                    <div class="history-item-time">${formatTimestamp(item.timestamp)}</div>
                </div>
                <div class="gl-item-actions">
                    <button class="gl-icon-btn history-btn-view" title="View Full">👁</button>
                    <button class="gl-icon-btn history-btn-del" title="Delete">🗑</button>
                </div>`;

            el.querySelector('.gl-item-text').addEventListener('click', () => {
                promptBox.value = item.text;
                promptBox.dispatchEvent(new Event('input'));
                historyModalEl.classList.remove('active');
            });

            el.querySelector('.history-btn-view').addEventListener('click', (e) => {
                e.stopPropagation();
                showHistoryViewer(item);
            });

            el.querySelector('.history-btn-del').addEventListener('click', (e) => {
                e.stopPropagation();
                if (currentHistoryTab === 'image') {
                    imagePromptHistory = imagePromptHistory.filter(h => h.id !== item.id);
                    GM_setValue('imagePromptHistory', imagePromptHistory);
                } else {
                    videoPromptHistory = videoPromptHistory.filter(h => h.id !== item.id);
                    GM_setValue('videoPromptHistory', videoPromptHistory);
                }
                renderHistory();
            });

            historyListContainer.appendChild(el);
        });
    }

    // --- HISTORY VIEWER ---
    function showHistoryViewer(item) {
        document.getElementById('history-view-list').style.display = 'none';
        document.getElementById('history-view-viewer').classList.add('active');
        document.getElementById('history-viewer-text').value = item.text;
        document.getElementById('history-viewer-time').textContent = formatTimestamp(item.timestamp);
        historyModalEl.dataset.currentItemId = item.id;
    }

    function closeHistoryViewer() {
        document.getElementById('history-view-viewer').classList.remove('active');
        document.getElementById('history-view-list').style.display = 'flex';
    }

    openHistoryBtn.addEventListener('click', () => {
        historyModalEl.classList.add('active');
        updateModalPosition();
        renderHistory();
    });

    clearHistoryBtn.addEventListener('click', () => {
        const tabName = currentHistoryTab === 'image' ? 'image' : 'video';
        if (confirm(`Clear all ${tabName} prompt history?`)) {
            if (currentHistoryTab === 'image') {
                imagePromptHistory = [];
                GM_setValue('imagePromptHistory', []);
            } else {
                videoPromptHistory = [];
                GM_setValue('videoPromptHistory', []);
            }
            renderHistory();
        }
    });

    historyModal.querySelector('.history-close').addEventListener('click', () => {
        historyModalEl.classList.remove('active');
        closeHistoryViewer();
    });

    document.getElementById('btn-viewer-back').addEventListener('click', closeHistoryViewer);

    document.getElementById('btn-viewer-use').addEventListener('click', () => {
        const text = document.getElementById('history-viewer-text').value;
        promptBox.value = text;
        promptBox.dispatchEvent(new Event('input'));
        historyModalEl.classList.remove('active');
        closeHistoryViewer();
    });

    // --- SNIPPETS MODAL LOGIC ---
    function renderSnippets() {
        listContainer.innerHTML = '';
        savedSnippets.forEach(item => {
            const el = document.createElement('div');
            el.className = 'gl-item';
            el.innerHTML = `
                <div class="gl-item-text"><b>${escapeHtml(item.label)}</b><span>${escapeHtml(item.text)}</span></div>
                <div class="gl-item-actions">
                    <button class="gl-icon-btn gl-btn-edit">✎</button>
                    <button class="gl-icon-btn gl-btn-del">🗑</button>
                </div>`;
            el.querySelector('.gl-item-text').addEventListener('click', () => {
                const cur = promptBox.value;
                promptBox.value = cur + (cur && !cur.endsWith(' ') ? ' ' : '') + item.text;
                promptBox.dispatchEvent(new Event('input'));
                modalEl.classList.remove('active');
            });
            el.querySelector('.gl-btn-edit').addEventListener('click', (e) => { e.stopPropagation(); showEditor(item); });
            el.querySelector('.gl-btn-del').addEventListener('click', (e) => {
                e.stopPropagation();
                if (confirm(`Delete "${item.label}"?`)) {
                    savedSnippets = savedSnippets.filter(s => s.id !== item.id);
                    GM_setValue('savedSnippets', savedSnippets);
                    renderSnippets();
                }
            });
            listContainer.appendChild(el);
        });
    }

    function showEditor(item = null) {
        document.getElementById('gl-view-list').style.display = 'none';
        document.getElementById('gl-view-editor').classList.add('active');
        editingId = item ? item.id : null;
        editLabel.value = item ? item.label : '';
        editText.value = item ? item.text : '';
        editText.focus();
    }

    createBtn.addEventListener('click', () => showEditor(null));

    document.getElementById('btn-edit-save').addEventListener('click', () => {
        const label = editLabel.value.trim() || 'Untitled';
        const text = editText.value.trim();
        if (!text) return alert("Empty text");
        if (editingId) {
            const idx = savedSnippets.findIndex(s => s.id === editingId);
            if (idx > -1) {
                savedSnippets[idx].label = label;
                savedSnippets[idx].text = text;
            }
        } else {
            savedSnippets.push({ id: Date.now().toString(), label, text });
        }
        GM_setValue('savedSnippets', savedSnippets);
        document.getElementById('gl-view-editor').classList.remove('active');
        document.getElementById('gl-view-list').style.display = 'flex';
        renderSnippets();
    });

    document.getElementById('btn-edit-cancel').addEventListener('click', () => {
        document.getElementById('gl-view-editor').classList.remove('active');
        document.getElementById('gl-view-list').style.display = 'flex';
    });

    openLibBtn.addEventListener('click', () => {
        modalEl.classList.add('active');
        updateModalPosition();
        renderSnippets();
    });

    modal.querySelector('.gl-close').addEventListener('click', () => modalEl.classList.remove('active'));

    function escapeHtml(text) {
        return text ? text.replace(/&/g, "&amp;").replace(/</g, "&lt;") : '';
    }

    // --- TOGGLE BUTTON ---
    toggleBtn.addEventListener('click', () => {
        isRetryEnabled = !isRetryEnabled;
        toggleBtn.textContent = isRetryEnabled ? "ON" : "OFF";
        toggleBtn.classList.toggle('off', !isRetryEnabled);
        resetState(isRetryEnabled ? "Ready" : "Disabled");
        if (!isRetryEnabled) statusText.className = 'status-error';
    });

    autoClickCb.addEventListener('change', (e) => {
        autoClickEnabled = e.target.checked;
        GM_setValue('autoClickEnabled', autoClickEnabled);
    });

    limitInput.addEventListener('change', (e) => {
        maxRetries = parseInt(e.target.value);
        GM_setValue('maxRetries', maxRetries);
    });

    // --- MUTATION OBSERVER ---
    const observer = new MutationObserver(() => {
        if (observerThrottle || !isRetryEnabled || limitReached || processingModeration) return;

        observerThrottle = true;
        setTimeout(() => { observerThrottle = false; }, OBSERVER_THROTTLE_MS);

        const toastContainer = document.querySelector('section[aria-label="Notifications alt+T"]');
        if (toastContainer) {
            const errorToast = toastContainer.querySelector('li[data-type="error"]');
            if (errorToast && !moderationDetected) {
                moderationDetected = true;
                setTimeout(() => {
                    try {
                        if (errorToast && errorToast.parentNode) {
                            errorToast.parentNode.removeChild(errorToast);
                        }
                    } catch (e) { /* Already removed */ }
                }, 2000);
            }
        }

        if (moderationDetected && !processingModeration) {
            const spans = document.querySelectorAll('span');
            let modSpan = null;
            for (let i = 0; i < spans.length; i++) {
                if (spans[i].textContent.trim() === MODERATION_TEXT) {
                    modSpan = spans[i];
                    break;
                }
            }

            if (modSpan) {
                processingModeration = true;
                const now = Date.now();

                setTimeout(() => {
                    try {
                        if (modSpan && modSpan.parentElement && modSpan.parentElement.parentNode) {
                            modSpan.parentElement.parentNode.removeChild(modSpan.parentElement);
                        }
                    } catch (e) { /* Already removed */ }
                    moderationDetected = false;
                    processingModeration = false;
                }, 2000);

                if (now - lastRetryTimestamp >= COOLDOWN_MS && now - lastGenerationTimestamp >= GENERATION_COOLDOWN_MS) {
                    handleRetry(now);
                }
            }
        }

        // Re-attach imagine observer if element appears
        startImagineObserver();

        // Check for video generation progress (5% indicator means video started)
        const progressEl = document.querySelector('.text-xs.font-semibold.w-\\[4ch\\].mb-\\[1px\\].tabular-nums');
        if (progressEl) {
            const txt = progressEl.textContent.trim();
            if (txt.includes('%')) {
                const val = parseInt(txt);
                if (!isNaN(val) && val >= 5 && val < 100) {
                    // Video generation detected - capture from video textarea
                    const videoTA = document.querySelector(TARGET_TEXTAREA_SELECTOR);
                    if (videoTA && videoTA.value.trim()) {
                        const prompt = videoTA.value.trim();
                        // Check if this prompt is already in video history
                        const existsInVideo = videoPromptHistory.some(item => item.text === prompt);
                        if (!existsInVideo) {
                            addToHistory(prompt, 'video');
                            updateStatus("Video prompt captured!");
                            setTimeout(() => {
                                if (isRetryEnabled) {
                                    updateStatus("Ready");
                                }
                            }, 2000);
                        }
                    }
                }
            }
        }
    });

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

    // --- RETRY HANDLER ---
    function handleRetry(now) {
        if (now - lastGenerationTimestamp < GENERATION_COOLDOWN_MS) return;

        lastRetryTimestamp = now;
        const grokTA = document.querySelector(TARGET_TEXTAREA_SELECTOR) ||
                       document.querySelector(IMAGE_EDITOR_SELECTOR) ||
                       document.querySelector(IMAGE_PROMPT_SELECTOR);
        const btn = document.querySelector(RETRY_BUTTON_SELECTOR) ||
                    document.querySelector(IMAGE_EDITOR_BUTTON_SELECTOR) ||
                    document.querySelector(IMAGE_SUBMIT_BUTTON_SELECTOR);
        const imagineP = document.querySelector(IMAGE_IMAGINE_SELECTOR);

        if ((grokTA || imagineP) && lastTypedPrompt) {
            if (grokTA) {
                nativeValueSet(grokTA, lastTypedPrompt);
            } else if (imagineP) {
                imagineP.textContent = lastTypedPrompt;
                if (imagineP.classList.contains('is-empty') && lastTypedPrompt) {
                    imagineP.classList.remove('is-empty');
                }
            }

            if (autoClickEnabled && currentRetryCount >= maxRetries) {
                updateStatus("Limit Reached", "error");
                limitReached = true;
                return;
            }

            if (autoClickEnabled && btn && !btn.disabled) {
                currentRetryCount++;
                updateStatus(`Retrying (${currentRetryCount}/${maxRetries})`);
                setTimeout(() => {
                    btn.click();
                    lastGenerationTimestamp = Date.now();
                }, RETRY_DELAY_MS);
            }
        }
    }

    // --- KEYBOARD TOGGLE ---
    document.addEventListener('keydown', (e) => {
        if (e.altKey && e.key.toLowerCase() === uiToggleKey) {
            isUiVisible = !isUiVisible;
            GM_setValue('isUiVisible', isUiVisible);
            panel.classList.toggle('hidden', !isUiVisible);
            e.preventDefault();
        }
    });

    // --- CLEANUP ---
    window.addEventListener('beforeunload', () => {
        observer.disconnect();
        imagineObserver.disconnect();
    });

})();