自動點擊生成 (Grok Imagine) v1.9

自動點擊生成影片按鈕,有使用者介面可調整閾值。

// ==UserScript==
// @name         自動點擊生成 (Grok Imagine) v1.9
// @namespace    http://tampermonkey.net/
// @version      1.9
// @description  自動點擊生成影片按鈕,有使用者介面可調整閾值。
// @match        https://grok.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // === 設定 ===
    const targetClassString = 'text-xs font-semibold w-[4ch] mb-[1px]';
    const selector = `div[class="${targetClassString}"]`;

    let lastValue = null;
    let wasPresent = false;
    let autoMode = true;
    let threshold = 30;
    let playBeepOnLow = true;
    let playBeepOnLimit = true;
    let beepVolume = 0.005;
    let limitAlertShown = false;
    let persistentSuccessNotify = false; // 新增: 成功後持續通知

    let zeroCount = 0;
    let consecutiveRetries = 0;
    let maxRetriesAlertShown = false; // 新增:記錄是否已經顯示過最大重試次數警報
    const zeroThreshold = 20; // seconds
    const checkInterval = 500; // ms
    const zeroMaxCount = Math.floor(zeroThreshold * 1000 / checkInterval);
    const maxConsecutiveRetries = 3; // 最多重試次數

    let worker = null;

    // 生成統計
    let stats = { total: 0, success: 0, fail: 0 };

    // === 輔助函式 ===
    function getTimeString() {
        const now = new Date();
        const pad = n => n.toString().padStart(2, '0');
        return `[${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}]`;
    }

    function parseNumber(text) {
        if (!text) return null;
        const match = text.match(/-?\d+(\.\d+)?/);
        return match ? parseFloat(match[0]) : null;
    }

    function beepTriple(frequency = 880, duration = 0.1, volume = beepVolume) {
        try {
            const ctx = new (window.AudioContext || window.webkitAudioContext)();
            const playBeep = () => {
                const oscillator = ctx.createOscillator();
                const gain = ctx.createGain();
                oscillator.connect(gain);
                gain.connect(ctx.destination);
                oscillator.type = 'square';
                oscillator.frequency.value = frequency;
                gain.gain.value = volume;
                oscillator.start();
                oscillator.stop(ctx.currentTime + duration);
            };
            playBeep();
            setTimeout(playBeep, duration * 1000 + 100);
            setTimeout(playBeep, 2 * (duration * 1000 + 100));
        } catch (e) {
            console.log(getTimeString(), '無法播放聲音:', e);
        }
    }

    function playCmaj7Arpeggio(volume = beepVolume, duration = 0.25) {
        try {
            const ctx = new (window.AudioContext || window.webkitAudioContext)();
            const d = 48;
            const notes = [16.35 * d, 20.6 * d, 24.5 * d, 30.87 * d]; // C, E, G, B
            notes.forEach((freq, i) => {
                const osc = ctx.createOscillator();
                const gain = ctx.createGain();
                osc.connect(gain);
                gain.connect(ctx.destination);
                osc.type = 'triangle';
                osc.frequency.value = freq;
                gain.gain.setValueAtTime(volume, ctx.currentTime + i * duration);
                osc.start(ctx.currentTime + i * duration);
                osc.stop(ctx.currentTime + (i + 1) * duration);
            });
        } catch (e) {
            console.log(getTimeString(), '無法播放 arpeggio:', e);
        }
    }

    // === 控制面板 ===
    function createControlPanel() {
        if (window.__grokImaginePanel) return window.__grokImaginePanel.querySelector('#grok-console');

        const panel = document.createElement('div');
        panel.style.position = 'fixed';
        panel.style.top = '80px';
        panel.style.right = '10px';
        panel.style.zIndex = '99999';
        panel.style.background = 'rgba(30,30,30,0.9)';
        panel.style.color = '#fff';
        panel.style.padding = '10px 12px';
        panel.style.borderRadius = '10px';
        panel.style.fontSize = '14px';
        panel.style.fontFamily = 'monospace';
        panel.style.boxShadow = '0 4px 12px rgba(0,0,0,0.6)';
        panel.style.backdropFilter = 'blur(5px)';
        panel.style.width = '360px';
        panel.style.maxHeight = '520px';
        panel.style.display = 'flex';
        panel.style.flexDirection = 'column';
        panel.style.pointerEvents = 'auto';
        panel.style.userSelect = 'none';

        // Header (用作拖曳)
        const header = document.createElement('div');
        header.style.display = 'flex';
        header.style.justifyContent = 'space-between';
        header.style.alignItems = 'center';
        header.style.cursor = 'move';
        header.style.marginBottom = '6px';
        header.style.gap = '8px';

        const title = document.createElement('div');
        title.style.fontWeight = 'bold';
        title.textContent = 'Grok 檢查控制';
        header.appendChild(title);

        // buttons group (minimize)
        const btnGroup = document.createElement('div');
        btnGroup.style.display = 'flex';
        btnGroup.style.alignItems = 'center';
        btnGroup.style.gap = '6px';

        const minimizeBtn = document.createElement('button');
        minimizeBtn.textContent = '-';
        minimizeBtn.title = '縮小/展開';
        minimizeBtn.style.background = 'transparent';
        minimizeBtn.style.border = 'none';
        minimizeBtn.style.color = '#fff';
        minimizeBtn.style.fontSize = '18px';
        minimizeBtn.style.cursor = 'pointer';
        minimizeBtn.style.padding = '0 6px';
        btnGroup.appendChild(minimizeBtn);

        header.appendChild(btnGroup);
        panel.appendChild(header);

        // 內容區塊(可縮放)
        const content = document.createElement('div');
        content.style.display = 'flex';
        content.style.flexDirection = 'column';
        content.style.gap = '6px';
        content.style.flex = '1';
        content.style.overflow = 'hidden';

        // Controls HTML (用 JS 建)
        const rowAuto = document.createElement('div');
        rowAuto.style.display = 'flex';
        rowAuto.style.alignItems = 'center';
        rowAuto.innerHTML = `<label style="display:flex;align-items:center;gap:6px;"><input type="checkbox" id="autoModeToggle" checked /> 自動模式</label>`;
        content.appendChild(rowAuto);

        const rowThreshold = document.createElement('div');
        rowThreshold.style.display = 'flex';
        rowThreshold.style.alignItems = 'center';
        rowThreshold.style.gap = '8px';
        rowThreshold.style.marginBottom = '2px';
        rowThreshold.innerHTML = `閥值(%):`;
        const thresholdInput = document.createElement('input');
        thresholdInput.type = 'number';
        thresholdInput.id = 'thresholdInput';
        thresholdInput.value = threshold;
        thresholdInput.min = 0;
        thresholdInput.max = 100;
        thresholdInput.step = 1;
        thresholdInput.style.width = '60px';
        thresholdInput.style.padding = '4px';
        thresholdInput.style.borderRadius = '4px';
        thresholdInput.style.border = 'none';
        thresholdInput.style.textAlign = 'center';

        const thresholdSlider = document.createElement('input');
        thresholdSlider.type = 'range';
        thresholdSlider.id = 'thresholdSlider';
        thresholdSlider.min = 0;
        thresholdSlider.max = 100;
        thresholdSlider.step = 1;
        thresholdSlider.value = threshold;
        thresholdSlider.style.flex = '1';

        rowThreshold.appendChild(thresholdInput);
        rowThreshold.appendChild(thresholdSlider);
        content.appendChild(rowThreshold);

        const rowBeep = document.createElement('div');
        rowBeep.style.display = 'flex';
        rowBeep.style.alignItems = 'center';
        rowBeep.innerHTML = `<label style="display:flex;align-items:center;gap:6px;"><input type="checkbox" id="beepToggle" checked /> 低於閾值播放音效</label>`;
        content.appendChild(rowBeep);

    const rowLimit = document.createElement('div');
    rowLimit.style.display = 'flex';
    rowLimit.style.alignItems = 'center';
    rowLimit.innerHTML = `<label style="display:flex;align-items:center;gap:6px;"><input type="checkbox" id="limitToggle" checked /> 額度用完提醒音效</label>`;
    content.appendChild(rowLimit);

    // 新增: 成功後持續通知勾選盒
    const rowPersistentSuccess = document.createElement('div');
    rowPersistentSuccess.style.display = 'flex';
    rowPersistentSuccess.style.alignItems = 'center';
    rowPersistentSuccess.innerHTML = `<label style="display:flex;align-items:center;gap:6px;"><input type="checkbox" id="persistentSuccessToggle" /> 成功後持續通知</label>`;
    content.appendChild(rowPersistentSuccess);

        // 音量 row 改百分比顯示
        const rowVolume = document.createElement('div');
        rowVolume.style.display = 'flex';
        rowVolume.style.alignItems = 'center';
        rowVolume.style.gap = '8px';
        rowVolume.innerHTML = `音量:`;
        const volumeSlider = document.createElement('input');
        volumeSlider.type = 'range';
        volumeSlider.id = 'volumeSlider';
        volumeSlider.min = 0;
        volumeSlider.max = 0.05;
        volumeSlider.step = 0.001;
        volumeSlider.value = beepVolume;
        volumeSlider.style.flex = '1';
        const volumeDisplay = document.createElement('span');
        volumeDisplay.id = 'volumeDisplay';
        volumeDisplay.textContent = Math.round(beepVolume / 0.05 * 100) + '%';
        rowVolume.appendChild(volumeSlider);
        rowVolume.appendChild(volumeDisplay);
        content.appendChild(rowVolume);

        // 統計顯示
        const statTitle = document.createElement('div');
        statTitle.style.fontWeight = 'bold';
        statTitle.textContent = '生成統計';
        content.appendChild(statTitle);

        const statRow = document.createElement('div');
        statRow.style.display = 'flex';
        statRow.style.gap = '10px';
        statRow.style.fontSize = '13px';
        statRow.innerHTML = `<span id="statTotal">總生成: 0</span><span id="statSuccess">成功: 0</span><span id="statFail">失敗: 0</span>`;
        content.appendChild(statRow);

        // console box
        const consoleBox = document.createElement('div');
        consoleBox.id = 'grok-console';
        consoleBox.style.flex = '1';
        consoleBox.style.background = 'rgba(0,0,0,0.28)';
        consoleBox.style.padding = '6px';
        consoleBox.style.borderRadius = '6px';
        consoleBox.style.overflowY = 'auto';
        consoleBox.style.fontSize = '12px';
        consoleBox.style.lineHeight = '1.4';
        consoleBox.style.whiteSpace = 'pre-wrap';
        consoleBox.style.minHeight = '120px';
        consoleBox.style.maxHeight = '250px'; // ⚡ 防止擠壓按鈕
        content.appendChild(consoleBox);

        // 按鈕列
        const btnRow = document.createElement('div');
        btnRow.style.display = 'flex';
        btnRow.style.gap = '8px';
        btnRow.style.marginTop = '6px';
        btnRow.style.justifyContent = 'flex-start';

        // 清空紀錄
        const clearBtn = document.createElement('button');
        clearBtn.textContent = '🧹 清空紀錄';
        clearBtn.style.background = 'rgba(255,255,255,0.06)';
        clearBtn.style.border = 'none';
        clearBtn.style.color = '#ccc';
        clearBtn.style.padding = '6px 10px';
        clearBtn.style.borderRadius = '6px';
        clearBtn.style.cursor = 'pointer';
        clearBtn.addEventListener('mouseenter', () => clearBtn.style.color = '#fff');
        clearBtn.addEventListener('mouseleave', () => clearBtn.style.color = '#ccc');
        clearBtn.addEventListener('click', () => {
            consoleBox.innerHTML = '';
            stats = { total: 0, success: 0, fail: 0 };
            if (window.__grokImaginePanel && window.__grokImaginePanel._updateStatsDisplay)
                window.__grokImaginePanel._updateStatsDisplay();
        });

        // 複製記錄
        const copyBtn = document.createElement('button');
        copyBtn.textContent = '📋 複製記錄';
        copyBtn.style.background = 'rgba(50,50,255,0.8)';
        copyBtn.style.border = 'none';
        copyBtn.style.color = '#fff';
        copyBtn.style.padding = '6px 10px';
        copyBtn.style.borderRadius = '6px';
        copyBtn.style.cursor = 'pointer';
        copyBtn.addEventListener('mouseenter', () => copyBtn.style.opacity = '0.85');
        copyBtn.addEventListener('mouseleave', () => copyBtn.style.opacity = '1');
        copyBtn.addEventListener('click', async () => {
            try {
                const text = consoleBox.innerText || '';
                const summary = `生成統計:總生成: ${stats.total},成功: ${stats.success},失敗: ${stats.fail}`;
                const finalText = text.trim() ? text + '\n\n' + summary : summary;
                await navigator.clipboard.writeText(finalText);
                console.log(`${getTimeString()} 已複製 console 記錄到剪貼簿`);
                console.log(summary);
            } catch (e) { console.log(`${getTimeString()} 複製失敗:`, e); }
        });

        // 登出按鈕
        const logoutBtn = document.createElement('button');
        logoutBtn.textContent = '🚪 登出';
        logoutBtn.style.background = 'rgba(255,50,50,0.8)';
        logoutBtn.style.border = 'none';
        logoutBtn.style.color = '#fff';
        logoutBtn.style.padding = '6px 10px';
        logoutBtn.style.borderRadius = '6px';
        logoutBtn.style.cursor = 'pointer';
        logoutBtn.addEventListener('mouseenter', () => logoutBtn.style.opacity = '0.85');
        logoutBtn.addEventListener('mouseleave', () => logoutBtn.style.opacity = '1');
        logoutBtn.addEventListener('click', tryLogout);

        btnRow.appendChild(clearBtn);
        btnRow.appendChild(copyBtn);
        btnRow.appendChild(logoutBtn);
        content.appendChild(btnRow);

        panel.appendChild(content);
        document.body.appendChild(panel);
        window.__grokImaginePanel = panel;

        // 變數綁定
    const autoModeToggle = panel.querySelector('#autoModeToggle');
    const thresholdInputEl = thresholdInput;
    const thresholdSliderEl = thresholdSlider;
    const beepToggle = panel.querySelector('#beepToggle');
    const limitToggle = panel.querySelector('#limitToggle');
    const persistentSuccessToggle = panel.querySelector('#persistentSuccessToggle');
    const volumeSliderEl = volumeSlider;
    const volumeDisplayEl = volumeDisplay;

        // hook console
        hookConsole(consoleBox);

        // 同步事件綁定
        autoModeToggle.addEventListener('change', () => { autoMode = autoModeToggle.checked; console.log(`${getTimeString()} 自動模式: ${autoMode ? '啟用' : '停用'}`); });

        thresholdInputEl.addEventListener('input', () => {
            let val = parseFloat(thresholdInputEl.value);
            if (isNaN(val)) val = 0;
            threshold = Math.min(Math.max(val, 0), 100);
            thresholdSliderEl.value = threshold;
        });
        thresholdSliderEl.addEventListener('input', () => {
            threshold = parseFloat(thresholdSliderEl.value);
            thresholdInputEl.value = threshold;
        });

    beepToggle.addEventListener('change', () => { playBeepOnLow = beepToggle.checked; });
    limitToggle.addEventListener('change', () => { playBeepOnLimit = limitToggle.checked; });
    persistentSuccessToggle.addEventListener('change', () => { persistentSuccessNotify = persistentSuccessToggle.checked; });

        volumeSliderEl.addEventListener('input', () => {
            beepVolume = parseFloat(volumeSliderEl.value);
            const percent = Math.round(beepVolume / 0.05 * 100);
            volumeDisplayEl.textContent = percent + '%';
        });

        function updateStatsDisplay() {
            const totalEl = panel.querySelector('#statTotal');
            const successEl = panel.querySelector('#statSuccess');
            const failEl = panel.querySelector('#statFail');
            if (totalEl) totalEl.textContent = `總生成: ${stats.total}`;
            if (successEl) successEl.textContent = `成功: ${stats.success}`;
            if (failEl) failEl.textContent = `失敗: ${stats.fail}`;
        }
        panel._updateStatsDisplay = updateStatsDisplay;

        // 拖曳功能 (保持在視窗內)
        let isDragging = false, offsetX = 0, offsetY = 0;
        header.addEventListener('mousedown', e => {
            isDragging = true;
            const rect = panel.getBoundingClientRect();
            offsetX = e.clientX - rect.left;
            offsetY = e.clientY - rect.top;
            panel.style.transition = 'none';
            document.body.style.userSelect = 'none';
        });
        document.addEventListener('mousemove', e => {
            if (!isDragging) return;
            let newLeft = e.clientX - offsetX;
            let newTop = e.clientY - offsetY;
            newLeft = Math.min(Math.max(newLeft, 0), window.innerWidth - panel.offsetWidth);
            newTop = Math.min(Math.max(newTop, 0), window.innerHeight - panel.offsetHeight);
            panel.style.left = newLeft + 'px';
            panel.style.top = newTop + 'px';
            panel.style.right = 'auto';
        });
        document.addEventListener('mouseup', () => {
            if (!isDragging) return;
            isDragging = false;
            panel.style.transition = '';
            document.body.style.userSelect = '';
        });

        // 縮小/展開
        let minimized = false;
        minimizeBtn.addEventListener('click', () => {
            minimized = !minimized;
            content.style.display = minimized ? 'none' : 'flex';
            minimizeBtn.textContent = minimized ? '+' : '-';
        });

        return consoleBox;
    }

    function hookConsole(consoleBox) {
        const originalLog = console.log;
        console.log = (...args) => {
            try {
                originalLog.apply(console, args);
                const msg = args.map(a => (typeof a === 'object' ? JSON.stringify(a) : String(a))).join(' ');
                const entry = document.createElement('div');
                if (msg.includes('成功')) entry.style.color = '#6eff9f';
                else if (msg.includes('失敗')) entry.style.color = '#ff7b7b';
                else entry.style.color = '#ffd966';
                entry.textContent = msg;
                consoleBox.appendChild(entry);
                consoleBox.scrollTop = consoleBox.scrollHeight;
            } catch (e) { originalLog('hookConsole error:', e); }
        };
    }

    // === 檢查邏輯 ===
    // 新增: 持續音效控制
    let persistentBeepInterval = null;
    function startPersistentBeep() {
        if (persistentBeepInterval) return;
        // 立即播放第一次
        playCmaj7Arpeggio(beepVolume);
        // 然後開始間隔播放
        persistentBeepInterval = setInterval(() => {
            playCmaj7Arpeggio(beepVolume);
        }, 2000); // 每2秒持續播放
    }
    function stopPersistentBeep() {
        if (persistentBeepInterval) {
            clearInterval(persistentBeepInterval);
            persistentBeepInterval = null;
        }
    }

    function showPersistentSuccessBox() {
        // 若已存在則不重複顯示
        if (document.getElementById('persistent-success-msgbox')) return;
        const box = document.createElement('div');
        box.id = 'persistent-success-msgbox';
        box.style.position = 'fixed';
        box.style.top = '50%';
        box.style.left = '50%';
        box.style.transform = 'translate(-50%, -50%)';
        box.style.zIndex = '100000';
        box.style.background = 'rgba(40,40,40,0.98)';
        box.style.color = '#fff';
        box.style.padding = '32px 36px';
        box.style.borderRadius = '16px';
        box.style.fontSize = '20px';
        box.style.fontFamily = 'monospace';
        box.style.boxShadow = '0 8px 32px rgba(0,0,0,0.7)';
        box.style.textAlign = 'center';
        box.innerHTML = `<div style="margin-bottom:18px;">成功提示!</div><button id="persistent-success-confirm" style="font-size:18px;padding:8px 24px;border-radius:8px;background:#6eff9f;color:#222;border:none;cursor:pointer;">停止</button>`;
        document.body.appendChild(box);
        const btn = box.querySelector('#persistent-success-confirm');
        btn.addEventListener('click', () => {
            stopPersistentBeep();
            box.remove();
        });
    }

    function check() {
        try {
            const upgradeElem = document.querySelector('span.text-secondary.font-medium');
            if (upgradeElem && upgradeElem.textContent.includes('Upgrade to unlock more')) {
                if (!limitAlertShown) {
                    limitAlertShown = true;
                    console.log(`${getTimeString()} 額度已用完!`);
                    if (playBeepOnLimit) beepTriple(440, 0.15);
                }
            } else { limitAlertShown = false; }

            const elem = document.querySelector(selector);
            if (elem) {
                wasPresent = true;
                const val = parseNumber(elem.textContent.trim());
                if (!isNaN(val)) lastValue = val;

                if (val === 0) {
                    zeroCount++;
                    if (zeroCount >= zeroMaxCount && autoMode) {
                        if (consecutiveRetries >= maxConsecutiveRetries) {
                            if (!maxRetriesAlertShown) {
                                console.log(`${getTimeString()} 已連續重試${consecutiveRetries}次仍卡在0%,停止自動重試`);
                                if (playBeepOnLimit) beepTriple(440, 0.15); // 播放提示音
                                maxRetriesAlertShown = true; // 標記已經顯示過警報
                            }
                            return;
                        }
                        const button = document.querySelector('button.bg-button-filled.inline-flex'); // 生成按鈕
                        if (button) { 
                            button.click(); 
                            console.log(`${getTimeString()} 長時間為0,已再次點擊生成按鈕 (第${consecutiveRetries + 1}次重試)`);
                            consecutiveRetries++;
                        }
                        else { console.log(`${getTimeString()} 長時間為0,但找不到生成按鈕`); }
                        zeroCount = 0;
                    }
                } else { 
                    zeroCount = 0;
                    consecutiveRetries = 0; // 當進度不為0時重置重試計數
                    maxRetriesAlertShown = false; // 重置警報標記
                }

            } else if (wasPresent) {
                wasPresent = false;
                stats.total++;
                const container = document.querySelector('div.relative.mx-auto.rounded-2xl.overflow-hidden');
                const progress = lastValue !== null ? `${lastValue}%` : "未知";
                let success = false;
                if (container) {
                    const grid = container.querySelector('div.grid');
                    if (grid && grid.querySelector('video#sd-video')) {
                        console.log(`${getTimeString()} 生成成功 (進度: ${progress})`);
                        success = true;
                        stats.success++;
                        if (persistentSuccessNotify) {
                            showPersistentSuccessBox();
                            startPersistentBeep();
                        } else {
                            playCmaj7Arpeggio(beepVolume);
                        }

                        // ===== 新增功能:生成成功後 10 秒內檢測 Content Moderated =====
                        const moderationCheckTime = 15000; // 10 秒
                        setTimeout(() => {
                            const moderatedElem = container.querySelector('span');
                            if (moderatedElem && moderatedElem.textContent.includes('Content Moderated. Try a different idea.')) {
                                const button = document.querySelector('button[aria-label="製作影片"]');
                                if (button) {
                                    button.click();
                                    console.log(`${getTimeString()} 生成成功後 15 秒檢測到 Content Moderated,自動再次點擊生成按鈕`);
                                } else {
                                    console.log(`${getTimeString()} 生成成功後 15 秒檢測到 Content Moderated,但找不到生成按鈕`);
                                }
                            }
                        }, moderationCheckTime);
                        // ===== End 新增功能 =====

                    }
                }
                if (!success) {
                    console.log(`${getTimeString()} 生成失敗 (進度: ${progress})`);
                    stats.fail++;
                    if (lastValue !== null && lastValue < threshold && playBeepOnLow) beepTriple();
                    if (autoMode && lastValue !== null && lastValue >= threshold) {
                        const button = document.querySelector('button.bg-button-filled.inline-flex'); // 生成按鈕
                        if (button) { button.click(); console.log(`${getTimeString()} 自動模式: 已重新點擊生成按鈕`); }
                        else { console.log(`${getTimeString()} 找不到生成按鈕,無法自動重新生成`); }
                    }
                }
                if (window.__grokImaginePanel && window.__grokImaginePanel._updateStatsDisplay) window.__grokImaginePanel._updateStatsDisplay();
                lastValue = null;
            }
        } catch (e) { console.log(getTimeString(), 'check() 發生錯誤:', e); }
    }

    function tryLogout() {
        const attemptLogout = () => {
            const sidebar = document.querySelector('div[data-variant="sidebar"][data-side="left"]');
            if (sidebar && sidebar.getAttribute('data-state') === 'expanded') {
                const triggerBtn = document.querySelector('button[data-sidebar="trigger"]');
                if (triggerBtn) { triggerBtn.click(); setTimeout(attemptLogout, 300); return; }
            }
            const trigger = document.querySelector('button[id^="radix-"][aria-haspopup="menu"]');
            if (trigger) {
                ['pointerdown','mousedown','mouseup','pointerup','click'].forEach(type => trigger.dispatchEvent(new MouseEvent(type,{bubbles:true,cancelable:true,view:window})));
                setTimeout(() => {
                    const allItems = document.querySelectorAll('div[role="menuitem"].flex.cursor-pointer');
                    const logoutBtn = allItems[allItems.length - 1]; // 通常登出是最後一個

                    if (logoutBtn) {
                        ['pointerdown','mousedown','mouseup','pointerup','click'].forEach(type =>
                            logoutBtn.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, view: window }))
                        );
                        console.log('✅ 已嘗試點擊登出');
                    } else {
                        console.log('⚠️ 找不到登出');
                        setTimeout(attemptLogout, 1000);
                    }
                }, 500);
            } else setTimeout(attemptLogout,1000);
        };
        attemptLogout();
    }

    function startChecker() {
        if (worker) return;
        const workerCode = `setInterval(()=>postMessage('tick'), ${checkInterval});`;
        const blob = new Blob([workerCode], { type: 'application/javascript' });
        worker = new Worker(URL.createObjectURL(blob));
        worker.onmessage = () => check();
    }

    function stopChecker() { if(worker) { worker.terminate(); worker = null; } }

    function isImaginePage() { return location.pathname.startsWith('/imagine/'); }

    function handlePageChange() {
        if (isImaginePage()) { if(window.__grokImaginePanel) window.__grokImaginePanel.style.display='flex'; startChecker(); }
        else { if(window.__grokImaginePanel) window.__grokImaginePanel.style.display='none'; stopChecker(); }
    }

    const consoleBox = createControlPanel();
    handlePageChange();

    let lastPath = location.pathname;
    const observer = new MutationObserver(()=>{ if(location.pathname !== lastPath){ lastPath = location.pathname; handlePageChange(); }});

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

document.addEventListener('visibilitychange',()=>{ if(!document.hidden) check(); });

})();