LiveReload

Randomized auto-refresh

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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();
    }
})();