LiveReload

Randomized auto-refresh

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

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

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

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

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

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