LiveReload

Randomized auto-refresh

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         LiveReload
// @namespace    https://github.com/RustwuIf
// @version      2.2.1
// @description  Randomized auto-refresh
// @author       Rustwulf
// @match        <all_urls>
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    function LiveReload() {
        const storageKey = 'autoRefreshState';
        const themeKey = 'autoRefreshTheme';
        const opacityKey = 'autoRefreshOpacity';
        const soundKey = 'autoRefreshSound';
        const defaults = { min: 5, max: 15, opacity: 1, sound: false };

        let isRunning = false;
        let isPausedBySafety = false;
        let isPausedByInactivity = false;
        let remainingTime = 0;
        let timerId = null;
        let maxRemainingTime = 0;
        let lastActivityTime = Date.now();
        let inactivityTimeout = null;
        const INACTIVITY_THRESHOLD = 60000;
        const originalFavicon = document.querySelector("link[rel~='icon']") ? document.querySelector("link[rel~='icon']").href : '/favicon.ico';

        const statsKey = 'autoRefreshStats';
        let stats = {
            totalReloads: 0,
            sessionStartTime: null,
            totalRunningTime: 0,
            waitTimes: [],
            historyLog: [],
            maxReloadsLimit: 0,
            excludedDomains: [],
            jitterMode: false,
            pauseOnNetwork: false
        };

        function loadStats() {
            try {
                const stored = localStorage.getItem(statsKey);
                if (stored) {
                    const loaded = JSON.parse(stored);
                    stats = { ...stats, ...loaded };
                }
            } catch (e) {
                console.error('[AutoRefresh] Stats load error:', e);
            }
        }

        function saveStats() {
            try {
                localStorage.setItem(statsKey, JSON.stringify(stats));
            } catch (e) {
                console.error('[AutoRefresh] Stats save error:', e);
            }
        }

        function getSettings() {
            return {
                min: parseInt(localStorage.getItem('minDelay'), 10) || defaults.min,
                max: parseInt(localStorage.getItem('maxDelay'), 10) || defaults.max,
                opacity: localStorage.getItem(opacityKey) || defaults.opacity,
                sound: localStorage.getItem(soundKey) === 'true'
            };
        }

        const updateFavicon = (number) => {
            const canvas = document.createElement('canvas');
            canvas.width = 32; canvas.height = 32;
            const ctx = canvas.getContext('2d');
            const img = new Image();
            img.crossOrigin = "Anonymous";
            img.src = originalFavicon;

            img.onload = () => {
                ctx.drawImage(img, 0, 0, 32, 32);
                ctx.beginPath();
                ctx.arc(20, 20, 12, 0, 2 * Math.PI);
                ctx.fillStyle = isPausedBySafety ? '#3498db' : (number <= 3 ? '#ff5f6d' : '#2ecc71');
                ctx.fill();
                ctx.fillStyle = "white";
                ctx.font = "bold 16px Arial";
                ctx.textAlign = "center";
                ctx.fillText(isPausedBySafety ? "!!" : number, 20, 26);

                let link = document.querySelector("link[rel~='icon']");
                if (!link) {
                    link = document.createElement('link');
                    link.rel = 'icon';
                    document.head.appendChild(link);
                }
                link.href = canvas.toDataURL('image/png');
            };

            img.onerror = () => {
                console.warn('[AutoRefresh] Could not load favicon');
            };
        };

        const showToast = (message) => {
            const toast = document.createElement('div');
            toast.style.cssText = 'position: fixed; bottom: 100px; left: 20px; background: rgba(52, 152, 219, 0.95); color: white; padding: 16px 24px; border-radius: 12px; font-weight: 600; z-index: 2147483646; box-shadow: 0 8px 25px rgba(0,0,0,0.3); backdrop-filter: blur(10px); animation: slideInUp 0.3s ease-out; font-family: Arial, sans-serif;';
            toast.textContent = message;
            document.body.appendChild(toast);
            setTimeout(() => toast.remove(), 2500);
        };

        const getReloadCount = () => {
            const count = localStorage.getItem('reloadCounter');
            return parseInt(count) || 0;
        };

        const incrementReloadCount = () => {
            const newCount = getReloadCount() + 1;
            localStorage.setItem('reloadCounter', newCount.toString());
            stats.totalReloads = newCount;
            saveStats();
            return newCount;
        };

        const resetReloadCount = () => {
            localStorage.setItem('reloadCounter', '0');
            stats.totalReloads = 0;
            saveStats();
        };

        const applyJitter = (value) => {
            if (!localStorage.getItem('jitterMode') || localStorage.getItem('jitterMode') !== 'true') return value;
            const jitter = Math.floor(value * 0.1);
            return value + Math.floor(Math.random() * jitter * 2) - jitter;
        };

        const isCurrentDomainExcluded = () => {
            if (!stats.excludedDomains || stats.excludedDomains.length === 0) return false;
            const currentDomain = window.location.hostname;
            return stats.excludedDomains.some(domain => currentDomain.includes(domain.trim()));
        };

        const addToHistory = () => {
            const now = new Date();
            const timeStr = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
            stats.historyLog.push(timeStr);
            if (stats.historyLog.length > 20) stats.historyLog.shift();
            saveStats();
        };

        const recordActivity = () => {
            lastActivityTime = Date.now();
            if (isPausedByInactivity) {
                isPausedByInactivity = false;
                updateUIState();
            }
            resetInactivityTimeout();
        };

        const resetInactivityTimeout = () => {
            if (inactivityTimeout) clearTimeout(inactivityTimeout);
            if (!isRunning || localStorage.getItem('inactivityPauseEnabled') !== 'true') return;

            inactivityTimeout = setTimeout(() => {
                if (isRunning && !isPausedBySafety) {
                    isPausedByInactivity = true;
                    updateUIState();
                }
            }, INACTIVITY_THRESHOLD);
        };

        let host, shadow, mainBtn, settingsBtn, themeBtn, settingsModal, opacitySlider;
        let minInput, maxInput, minDisplay, maxDisplay;

        function createShadowUI() {
            if (!document.body) {
                setTimeout(createShadowUI, 100);
                return;
            }

            host = document.createElement('div');
            host.id = 'live-reload-host';
            host.style.cssText = 'position: fixed; bottom: 20px; left: 20px; z-index: 2147483647; font-family: "Inter", sans-serif;';

            let isDragging = false;
            let dragOffsetX = 0;
            let dragOffsetY = 0;

            host.addEventListener('mousedown', (e) => {
                if (e.button !== 0) return;
                const path = e.composedPath();
                const isFromModal = path.some(el => {
                    return el.tagName === 'INPUT' ||
                           (el.className && (el.className.includes('modal') || el.className.includes('stats-section')));
                });
                if (isFromModal) return;

                isDragging = true;
                dragOffsetX = e.clientX - host.offsetLeft;
                dragOffsetY = e.clientY - host.offsetTop;
                host.style.cursor = 'grabbing';
            });

            document.addEventListener('mousemove', (e) => {
                if (!isDragging) return;
                host.style.left = (e.clientX - dragOffsetX) + 'px';
                host.style.top = (e.clientY - dragOffsetY) + 'px';
                host.style.bottom = 'auto';
                localStorage.setItem('liveReloadPos', JSON.stringify({x: e.clientX - dragOffsetX, y: e.clientY - dragOffsetY}));
            });

            document.addEventListener('mouseup', () => {
                isDragging = false;
                host.style.cursor = 'grab';
            });

            const savedPos = localStorage.getItem('liveReloadPos');
            if (savedPos) {
                try {
                    const pos = JSON.parse(savedPos);
                    host.style.left = pos.x + 'px';
                    host.style.top = pos.y + 'px';
                    host.style.bottom = 'auto';
                } catch (e) {
                    console.warn('[AutoRefresh] Could not restore position:', e);
                }
            }

            document.body.appendChild(host);
            shadow = host.attachShadow({ mode: 'open' });

            const style = document.createElement('style');
            style.textContent = `
                :host { --accent-red: linear-gradient(135deg, #ff5f6d, #ffc371); --accent-green: linear-gradient(135deg, #11998e, #38ef7d); --accent-blue: #3498db; }
                :host([theme="dark"]) { --bg: rgba(20,20,25,0.95); --modal-bg: rgba(15,15,20,0.98); --text: #fff; --border: rgba(255,255,255,0.08); --input-bg: rgba(255,255,255,0.05); }
                :host([theme="light"]) { --bg: rgba(240,242,245,0.95); --modal-bg: rgba(255,255,255,0.98); --text: #1a1a1b; --border: rgba(0,0,0,0.08); --input-bg: rgba(0,0,0,0.03); }

                .btn { cursor: pointer; color: var(--text); border-radius: 12px; padding: 11px 20px; font-weight: 600; border: 1px solid var(--border); display: flex; align-items: center; gap: 8px; backdrop-filter: blur(20px); background: var(--bg); transition: all 0.25s ease; font-size: 14px; }
                .btn:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,0.15); border-color: rgba(255,255,255,0.15); }
                .btn:active { transform: translateY(0px); }
                .btn:hover .icon { animation: iconRotate 0.6s ease-in-out forwards; }
                .icon { display: inline-block; font-size: 16px; }

                .btn-main { background: var(--accent-red); border: none; color: white; min-width: 130px; justify-content: center; box-shadow: 0 6px 20px rgba(255, 95, 109, 0.3); position: relative; font-size: 15px; font-weight: 700; }
                .btn-main::before {
                    content: '';
                    position: absolute;
                    top: 50%;
                    left: 50%;
                    transform: translate(-50%, -50%);
                    width: 160px;
                    height: 160px;
                    border: 3px solid rgba(255, 255, 255, 0.2);
                    border-radius: 50%;
                    animation: rotatePulse 3s linear infinite;
                    pointer-events: none;
                    z-index: -1;
                }
                .btn-main.active::before {
                    border-color: rgba(255, 255, 255, 0.7);
                    animation: rotatePulse 2s linear infinite;
                }
                .btn-main.safety::before {
                    border-color: rgba(52, 152, 219, 0.7);
                    animation: rotatePulse 1.5s linear infinite;
                }
                @keyframes rotatePulse {
                    0% { transform: translate(-50%, -50%) rotate(0deg); }
                    100% { transform: translate(-50%, -50%) rotate(360deg); }
                }
                .btn-main.active { background: linear-gradient(135deg, #f1c40f, #f39c12); color: #000; box-shadow: 0 6px 20px rgba(241, 196, 15, 0.4); }
                .btn-main.safety { background: var(--accent-blue); color: white; box-shadow: 0 6px 20px rgba(52, 152, 219, 0.3); animation: softPulse 2s infinite; }

                .modal {
                    position: absolute; bottom: 75px; left: 0; background: var(--modal-bg); padding: 18px; border-radius: 18px;
                    border: 1px solid var(--border); box-shadow: 0 25px 60px rgba(0,0,0,0.2); color: var(--text); display: none;
                    flex-direction: column; gap: 10px; width: 300px; backdrop-filter: blur(30px); max-height: 480px; overflow-y: auto;
                }
                .modal::-webkit-scrollbar { width: 5px; }
                .modal::-webkit-scrollbar-track { background: transparent; }
                .modal::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 3px; }
                .modal::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.3); }

                .modal, .modal * { cursor: auto !important; }
                .modal button { cursor: pointer !important; }
                .modal input { cursor: pointer !important; }

                input[type="range"] {
                    width: 100%; height: 6px; border-radius: 5px; background: var(--input-bg);
                    outline: none; -webkit-appearance: none; appearance: none;
                    accent-color: #11998e;
                }
                input[type="range"]::-webkit-slider-thumb {
                    -webkit-appearance: none; appearance: none; width: 18px; height: 18px; border-radius: 50%;
                    background: linear-gradient(135deg, #11998e, #38ef7d); cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.2);
                    transition: all 0.2s ease;
                }
                input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.15); box-shadow: 0 4px 12px rgba(17,153,142,0.4); }
                input[type="range"]::-moz-range-thumb {
                    width: 18px; height: 18px; border-radius: 50%; background: linear-gradient(135deg, #11998e, #38ef7d);
                    cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.2); border: none; transition: all 0.2s ease;
                }
                input[type="range"]::-moz-range-thumb:hover { transform: scale(1.15); box-shadow: 0 4px 12px rgba(17,153,142,0.4); }

                input[type="checkbox"] {
                    width: 16px; height: 16px; cursor: pointer; accent-color: #11998e;
                }

                .preset-btn {
                    background: rgba(17, 153, 142, 0.15); border: 1px solid rgba(17, 153, 142, 0.3); color: var(--text);
                    padding: 8px 12px; border-radius: 8px; font-weight: 600; font-size: 11px; cursor: pointer;
                    transition: all 0.2s ease; font-family: inherit;
                }
                .preset-btn:hover { background: rgba(17, 153, 142, 0.25); border-color: rgba(17, 153, 142, 0.5); transform: translateY(-1px); }
                .preset-btn:active { transform: translateY(0); }

                .btn-save {
                    background: var(--accent-green); border: none; color: white; padding: 10px 35px; border-radius: 10px;
                    font-weight: 700; margin: 8px auto 0 auto; display: block; cursor: pointer; font-size: 13px;
                    transition: all 0.2s ease; box-shadow: 0 4px 15px rgba(17,153,142,0.3); font-family: inherit;
                }
                .btn-save:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(17,153,142,0.4); }
                .btn-save:active { transform: translateY(0); }

                .section-title { font-weight: 700; font-size: 11px; text-transform: uppercase; letter-spacing: 0.4px; opacity: 0.65; margin: 6px 0 2px 0; }
                .input-group { display: flex; flex-direction: column; gap: 5px; }
                .input-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
                .input-row label { font-size: 12px; font-weight: 500; }
                .input-row strong { color: #11998e; font-weight: 700; font-size: 13px; }

                .divider { height: 1px; background: var(--border); margin: 8px 0; }
                .presets-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; }

                .stats-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; font-size: 11px; }
                .stats-grid > div { padding: 6px; background: var(--input-bg); border-radius: 6px; border: 1px solid var(--border); }
                .stats-grid .label { opacity: 0.6; margin-bottom: 2px; }
                .stats-grid .value { font-size: 13px; font-weight: 700; color: #11998e; }

                @keyframes softPulse { 0% { opacity: 1; } 50% { opacity: 0.75; } 100% { opacity: 1; } }
                @keyframes iconRotate { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
                @keyframes slideInUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
            `;

            const wrapper = document.createElement('div');
            wrapper.style.opacity = getSettings().opacity;
            const container = document.createElement('div');
            container.style.display = 'flex'; container.style.gap = '10px';

            mainBtn = document.createElement('button');
            mainBtn.className = 'btn btn-main';
            mainBtn.style.position = 'relative';
            mainBtn.innerHTML = `<span class="icon">▶</span><span>Start</span>`;

            settingsBtn = document.createElement('button');
            settingsBtn.className = 'btn';
            settingsBtn.innerHTML = `<span class="icon">⚙</span>`;

            themeBtn = document.createElement('button');
            themeBtn.className = 'btn';

            settingsModal = document.createElement('div');
            settingsModal.className = 'modal';
            settingsModal.innerHTML = `
                <div style="font-weight: 700; font-size: 13px;">⚡ Presets</div>
                <div class="presets-grid">
                    <button class="preset-btn" id="preset-1-5">1-5s</button>
                    <button class="preset-btn" id="preset-1-15">1-15s</button>
                    <button class="preset-btn" id="preset-5-10">5-10s</button>
                    <button class="preset-btn" id="preset-10-15">10-15s</button>
                    <button class="preset-btn" id="preset-15-20">15-20s</button>
                    <button class="preset-btn" id="preset-20-30">20-30s</button>
                    <button class="preset-btn" id="preset-1-5m" style="grid-column: 1 / -1;">1-5m</button>
                </div>

                <div class="divider"></div>

                <div class="section-title">⏱ Timing</div>
                <div class="input-group">
                    <div class="input-row">
                        <label>Min</label>
                        <strong id="min-display">5</strong><span style="opacity: 0.5; font-size: 11px;">s</span>
                    </div>
                    <input type="range" id="min-input" min="1" max="299" value="5">
                </div>

                <div class="input-group">
                    <div class="input-row">
                        <label>Max</label>
                        <strong id="max-display">15</strong><span style="opacity: 0.5; font-size: 11px;">s</span>
                    </div>
                    <input type="range" id="max-input" min="2" max="300" value="15">
                </div>

                <div class="divider"></div>

                <div class="section-title">🎛 Controls</div>
                <div class="input-group">
                    <div class="input-row">
                        <label>Max Reloads</label>
                        <strong id="max-reloads-display">0</strong>
                    </div>
                    <input type="range" id="max-reloads-input" min="0" max="100" value="0">
                </div>

                <div class="input-group">
                    <div class="input-row">
                        <label>Opacity</label>
                        <strong id="opacity-display">100</strong><span style="opacity: 0.5; font-size: 11px;">%</span>
                    </div>
                    <input type="range" id="opacity-slider" min="0.2" max="1" step="0.1" value="1">
                </div>

                <div style="display: flex; gap: 10px; margin-top: 6px;">
                    <div style="flex: 1; display: flex; align-items: center; gap: 6px;">
                        <input type="checkbox" id="inactivity-toggle" style="margin: 0;">
                        <label style="font-size: 11px; font-weight: 500; margin: 0;">Inactivity</label>
                    </div>
                    <div style="flex: 1; display: flex; align-items: center; gap: 6px;">
                        <input type="checkbox" id="jitter-toggle" style="margin: 0;">
                        <label style="font-size: 11px; font-weight: 500; margin: 0;">Jitter</label>
                    </div>
                </div>

                <div class="divider"></div>

                <div class="section-title">📊 Stats</div>
                <div class="stats-grid">
                    <div>
                        <div class="label">Reloads</div>
                        <div class="value" id="stat-reloads">0</div>
                    </div>
                    <div>
                        <div class="label">Limit</div>
                        <div class="value" id="stat-max-limit">0</div>
                    </div>
                    <div>
                        <div class="label">Session</div>
                        <div class="value" id="stat-session">0m</div>
                    </div>
                    <div>
                        <div class="label">Avg</div>
                        <div class="value" id="stat-avg">0s</div>
                    </div>
                </div>

                <button class="btn-save" id="save-btn">SAVE</button>
                <button class="preset-btn" id="reset-stats-btn" style="width: 100%; margin-top: 4px; padding: 8px; font-size: 11px;">Reset</button>
            `;

            container.append(mainBtn, themeBtn, settingsBtn);
            wrapper.append(container, settingsModal);
            shadow.append(style, wrapper);

            minInput = settingsModal.querySelector('#min-input');
            maxInput = settingsModal.querySelector('#max-input');
            minDisplay = settingsModal.querySelector('#min-display');
            maxDisplay = settingsModal.querySelector('#max-display');

            opacitySlider = settingsModal.querySelector('#opacity-slider');

            const maxReloadsInput = settingsModal.querySelector('#max-reloads-input');
            const maxReloadsDisplay = settingsModal.querySelector('#max-reloads-display');
            const opacityDisplay = settingsModal.querySelector('#opacity-display');

            // MIN SLIDER - Real-time + Validation
            minInput.addEventListener('input', function() {
                minDisplay.textContent = this.value;
            });

            minInput.addEventListener('change', function() {
                const minVal = parseInt(this.value, 10);
                const maxVal = parseInt(maxInput.value, 10);
                if (minVal >= maxVal) {
                    this.value = maxVal - 1;
                    minDisplay.textContent = maxVal - 1;
                    console.log('[AutoRefresh] Min auto-corrected to:', maxVal - 1);
                }
            });

            // MAX SLIDER - Real-time + Validation
            maxInput.addEventListener('input', function() {
                maxDisplay.textContent = this.value;
            });

            maxInput.addEventListener('change', function() {
                const maxVal = parseInt(this.value, 10);
                const minVal = parseInt(minInput.value, 10);
                if (maxVal <= minVal) {
                    this.value = minVal + 1;
                    maxDisplay.textContent = minVal + 1;
                    console.log('[AutoRefresh] Max auto-corrected to:', minVal + 1);
                }
            });

            // MAX RELOADS SLIDER
            maxReloadsInput.addEventListener('input', function() {
                maxReloadsDisplay.textContent = this.value;
            });

            // OPACITY SLIDER
            opacitySlider.addEventListener('input', function() {
                wrapper.style.opacity = this.value;
                opacityDisplay.textContent = Math.round(this.value * 100);
            });
        }

        function updateUIState() {
            if (isPausedByInactivity) {
                mainBtn.className = 'btn btn-main safety';
                mainBtn.innerHTML = `<span class="icon">💤</span><span>NO ACTIVITY</span>`;
            } else if (isPausedBySafety) {
                mainBtn.className = 'btn btn-main safety';
                mainBtn.innerHTML = `<span class="icon">🛡</span><span>PAUSED</span>`;
            } else if (isRunning) {
                mainBtn.className = 'btn btn-main active';
                mainBtn.innerHTML = `<span class="icon">⏸</span><span>${remainingTime}s</span>`;
            } else {
                mainBtn.className = 'btn btn-main';
                mainBtn.innerHTML = `<span class="icon">▶</span><span>Start</span>`;
            }
            updateFavicon(remainingTime);
            if (isRunning && !isPausedBySafety && !isPausedByInactivity) {
                document.title = `[${remainingTime}s] ` + document.title.replace(/\[\d+s\]\s/, '');
            } else {
                document.title = document.title.replace(/\[\d+s\]\s/, '');
            }
        }

        function startCycle() {
            if (isCurrentDomainExcluded()) {
                showToast('⛔ Domain excluded');
                isRunning = false;
                updateUIState();
                return;
            }

            const { min, max } = getSettings();
            let waitTime = Math.max(1, Math.floor(Math.random() * (max - min) + min));
            waitTime = applyJitter(waitTime);
            remainingTime = waitTime;
            maxRemainingTime = remainingTime;
            stats.waitTimes.push(remainingTime);

            if (!stats.sessionStartTime) {
                stats.sessionStartTime = Date.now();
            }

            isPausedByInactivity = false;
            resetInactivityTimeout();
            updateUIState();

            timerId = setInterval(() => {
                if (!isPausedBySafety && !isPausedByInactivity) {
                    remainingTime--;
                    updateUIState();
                    if (remainingTime <= 0) {
                        const maxReloadsLimit = parseInt(localStorage.getItem('maxReloadsLimit')) || 0;
                        const currentReloadCount = getReloadCount();

                        if (maxReloadsLimit > 0 && currentReloadCount >= maxReloadsLimit) {
                            showToast('🛑 Max reached');
                            isRunning = false;
                            clearInterval(timerId);
                            updateUIState();
                            return;
                        }

                        incrementReloadCount();
                        addToHistory();
                        window.location.reload();
                    }
                }
            }, 1000);
        }

        const checkSafety = (e) => {
            const active = document.activeElement;
            const isTyping = active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.isContentEditable);
            if (isRunning && isTyping) {
                isPausedBySafety = true;
            } else {
                isPausedBySafety = false;
            }
            updateUIState();
        };

        createShadowUI();
        host.setAttribute('theme', localStorage.getItem(themeKey) || 'dark');
        themeBtn.innerHTML = host.getAttribute('theme') === 'dark' ? `<span class="icon">☀️</span>` : `<span class="icon">🌙</span>`;

        mainBtn.onclick = () => {
            isRunning = !isRunning;
            isPausedBySafety = false;
            isPausedByInactivity = false;
            localStorage.setItem(storageKey, isRunning);
            isRunning ? startCycle() : (clearInterval(timerId), clearTimeout(inactivityTimeout), updateUIState());
        };

        document.addEventListener('focusin', checkSafety);
        document.addEventListener('focusout', () => setTimeout(checkSafety, 100));
        document.addEventListener('mousemove', recordActivity);
        document.addEventListener('keypress', recordActivity);
        document.addEventListener('click', recordActivity);
        document.addEventListener('keydown', (e) => {
            if (e.shiftKey && e.key.toUpperCase() === 'T') {
                e.preventDefault();
                mainBtn.click();
            }
        });

        loadStats();
        stats.totalReloads = getReloadCount();

        themeBtn.onclick = () => {
            const next = host.getAttribute('theme') === 'dark' ? 'light' : 'dark';
            host.setAttribute('theme', next);
            localStorage.setItem(themeKey, next);
            themeBtn.innerHTML = next === 'dark' ? `<span class="icon">☀️</span>` : `<span class="icon">🌙</span>`;
        };

        settingsBtn.onclick = () => {
            settingsModal.style.display = settingsModal.style.display === 'flex' ? 'none' : 'flex';
            const s = getSettings();

            minInput.value = s.min;
            maxInput.value = s.max;
            minDisplay.textContent = s.min;
            maxDisplay.textContent = s.max;

            shadow.querySelector('#inactivity-toggle').checked = localStorage.getItem('inactivityPauseEnabled') === 'true';
            shadow.querySelector('#jitter-toggle').checked = localStorage.getItem('jitterMode') === 'true';

            const maxReloadsValue = localStorage.getItem('maxReloadsLimit') || '0';
            shadow.querySelector('#max-reloads-input').value = maxReloadsValue;
            shadow.querySelector('#max-reloads-display').textContent = maxReloadsValue;

            opacitySlider.value = s.opacity;
            shadow.querySelector('#opacity-display').textContent = Math.round(s.opacity * 100);

            const sessionTime = stats.sessionStartTime ? Date.now() - stats.sessionStartTime : 0;
            const minutes = Math.floor(sessionTime / 60000);
            const seconds = Math.floor((sessionTime % 60000) / 1000);
            const avgWait = stats.waitTimes.length > 0 ? Math.round(stats.waitTimes.reduce((a,b) => a+b) / stats.waitTimes.length) : 0;

            shadow.querySelector('#stat-reloads').textContent = stats.totalReloads;
            shadow.querySelector('#stat-max-limit').textContent = localStorage.getItem('maxReloadsLimit') || '0';
            shadow.querySelector('#stat-session').textContent = minutes > 0 ? `${minutes}m` : '0m';
            shadow.querySelector('#stat-avg').textContent = avgWait + 's';

            console.log('[AutoRefresh] Settings opened');
        };

        // Presets
        const updateSliderDisplay = (minVal, maxVal) => {
            minInput.value = minVal;
            maxInput.value = maxVal;
            minDisplay.textContent = minVal;
            maxDisplay.textContent = maxVal;
            console.log(`[AutoRefresh] Preset: ${minVal}s - ${maxVal}s`);
        };

        shadow.querySelector('#preset-1-5').addEventListener('click', () => updateSliderDisplay(1, 5));
        shadow.querySelector('#preset-1-15').addEventListener('click', () => updateSliderDisplay(1, 15));
        shadow.querySelector('#preset-5-10').addEventListener('click', () => updateSliderDisplay(5, 10));
        shadow.querySelector('#preset-10-15').addEventListener('click', () => updateSliderDisplay(10, 15));
        shadow.querySelector('#preset-15-20').addEventListener('click', () => updateSliderDisplay(15, 20));
        shadow.querySelector('#preset-20-30').addEventListener('click', () => updateSliderDisplay(20, 30));
        shadow.querySelector('#preset-1-5m').addEventListener('click', () => updateSliderDisplay(60, 300));

        shadow.querySelector('#save-btn').addEventListener('click', () => {
            const minVal = parseInt(minInput.value, 10);
            const maxVal = parseInt(maxInput.value, 10);

            if (minVal >= maxVal) {
                showToast('❌ Min must be less than Max');
                console.warn('[AutoRefresh] Save validation failed');
                return;
            }

            localStorage.setItem('minDelay', minVal.toString());
            localStorage.setItem('maxDelay', maxVal.toString());
            localStorage.setItem(opacityKey, opacitySlider.value);
            localStorage.setItem('inactivityPauseEnabled', shadow.querySelector('#inactivity-toggle').checked);
            localStorage.setItem('jitterMode', shadow.querySelector('#jitter-toggle').checked);

            const maxReloadsValue = parseInt(shadow.querySelector('#max-reloads-input').value) || 0;
            localStorage.setItem('maxReloadsLimit', maxReloadsValue.toString());

            stats.maxReloadsLimit = maxReloadsValue;
            stats.jitterMode = shadow.querySelector('#jitter-toggle').checked;
            saveStats();

            showToast('✅ Settings saved!');
            console.log('[AutoRefresh] Settings saved');
            settingsModal.style.display = 'none';
        });

        shadow.querySelector('#reset-stats-btn').addEventListener('click', () => {
            if (confirm('Reset all statistics?')) {
                resetReloadCount();
                stats = {
                    totalReloads: 0,
                    sessionStartTime: null,
                    totalRunningTime: 0,
                    waitTimes: [],
                    historyLog: [],
                    maxReloadsLimit: 0,
                    excludedDomains: [],
                    jitterMode: false,
                    pauseOnNetwork: false
                };
                saveStats();
                shadow.querySelector('#stat-reloads').textContent = '0';
                shadow.querySelector('#stat-max-limit').textContent = '0';
                shadow.querySelector('#stat-session').textContent = '0m';
                shadow.querySelector('#stat-avg').textContent = '0s';
                showToast('🔄 Stats reset!');
            }
        });

        if (localStorage.getItem(storageKey) === 'true') {
            isRunning = true;
            startCycle();
        }
    }

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