Greasy Fork

Greasy Fork is available in English.

Universal HTML5 Speed Hack (Pro Enhanced)

Lets you change the speed of any website. Made with ChatGPT, later enhanced by Claude.

当前为 2025-07-27 提交的版本,查看 最新版本

// ==UserScript==
// @name         Universal HTML5 Speed Hack (Pro Enhanced)
// @namespace    http://greasyfork.icu/users/1498931-kubabreak
// @version      2.1
// @description  Lets you change the speed of any website. Made with ChatGPT, later enhanced by Claude.
// @author       kubabreak
// @match        *://*/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Configuration
    const CONFIG = {
        defaultSpeed: 1.0,
        minSpeed: 0.001,
        maxSpeed: 100.0,
        step: 0.001,
        uiUpdateInterval: 100,
        favoritePresets: [0.1, 0.25, 0.5, 1, 2, 5, 10]
    };

    // Global state
    let globalSpeed = CONFIG.defaultSpeed;
    let uiElement = null;
    let isInitialized = false;
    let startTime = Date.now();

    const globalState = {
        perfNow: false,
        dateNow: false,
        setTimeout: false,
        setInterval: false,
        raf: false,
        clearTimeouts: true,
        customPresets: [],
        debugLogging: false,
        autoInjectFrames: true,
        persistentUI: true
    };

    // Storage for persistence
    const STORAGE_KEY = 'speedhack_settings';

    function saveSettings() {
        try {
            const settings = {
                speed: globalSpeed,
                state: globalState,
                uiPosition: uiElement ? {
                    left: uiElement.style.left,
                    top: uiElement.style.top
                } : null
            };
            localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
        } catch (e) {
            console.warn('[SpeedHack] Could not save settings:', e);
        }
    }

    function loadSettings() {
        try {
            const saved = localStorage.getItem(STORAGE_KEY);
            if (saved) {
                const settings = JSON.parse(saved);
                globalSpeed = settings.speed || CONFIG.defaultSpeed;
                Object.assign(globalState, settings.state || {});
                return settings;
            }
        } catch (e) {
            console.warn('[SpeedHack] Could not load settings:', e);
        }
        return null;
    }

    function createInjectionScript(speed, state) {
        return `
        (function() {
            if (window.__speedHackInjected) return;
            window.__speedHackInjected = true;

            const realPerfNow = performance.now.bind(performance);
            const realDateNow = Date.now;
            const realSetTimeout = window.setTimeout;
            const realSetInterval = window.setInterval;
            const realClearTimeout = window.clearTimeout;
            const realClearInterval = window.clearInterval;
            const realRAF = window.requestAnimationFrame;
            const realCAF = window.cancelAnimationFrame;

            let currentSpeed = ${speed};
            let perfStartTime = realPerfNow();
            let virtualStartTime = perfStartTime;
            let dateStartTime = realDateNow();
            let virtualDateStart = dateStartTime;

            const timeoutMap = new Map();
            const intervalMap = new Map();
            let timeoutId = 1;
            let intervalId = 1;

            // Performance.now override
            ${state.perfNow ? `
            performance.now = function() {
                const realNow = realPerfNow();
                const realElapsed = realNow - perfStartTime;
                return virtualStartTime + (realElapsed * currentSpeed);
            };
            ` : ''}

            // Date.now override
            ${state.dateNow ? `
            Date.now = function() {
                const realNow = realPerfNow();
                const realElapsed = realNow - perfStartTime;
                return Math.floor(virtualDateStart + (realElapsed * currentSpeed));
            };
            ` : ''}

            // setTimeout override
            ${state.setTimeout ? `
            window.setTimeout = function(callback, delay, ...args) {
                if (typeof callback !== 'function') return realSetTimeout(callback, delay, ...args);

                const adjustedDelay = Math.max(0, delay / currentSpeed);
                const id = timeoutId++;
                const realId = realSetTimeout(() => {
                    timeoutMap.delete(id);
                    callback.apply(this, args);
                }, adjustedDelay);

                timeoutMap.set(id, realId);
                return id;
            };

            ${state.clearTimeouts ? `
            window.clearTimeout = function(id) {
                const realId = timeoutMap.get(id);
                if (realId !== undefined) {
                    timeoutMap.delete(id);
                    return realClearTimeout(realId);
                }
                return realClearTimeout(id);
            };
            ` : ''}
            ` : ''}

            // setInterval override
            ${state.setInterval ? `
            window.setInterval = function(callback, delay, ...args) {
                if (typeof callback !== 'function') return realSetInterval(callback, delay, ...args);

                const adjustedDelay = Math.max(1, delay / currentSpeed);
                const id = intervalId++;
                const realId = realSetInterval(() => {
                    callback.apply(this, args);
                }, adjustedDelay);

                intervalMap.set(id, realId);
                return id;
            };

            ${state.clearTimeouts ? `
            window.clearInterval = function(id) {
                const realId = intervalMap.get(id);
                if (realId !== undefined) {
                    intervalMap.delete(id);
                    return realClearInterval(realId);
                }
                return realClearInterval(id);
            };
            ` : ''}
            ` : ''}

            // requestAnimationFrame override
            ${state.raf ? `
            window.requestAnimationFrame = function(callback) {
                return realRAF((timestamp) => {
                    const virtualTime = performance.now();
                    callback(virtualTime);
                });
            };
            ` : ''}

            // Update speed function
            window.__updateSpeedHack = function(newSpeed, newState) {
                currentSpeed = newSpeed;

                // Update time references to prevent jumps
                const realNow = realPerfNow();
                const currentVirtualTime = virtualStartTime + ((realNow - perfStartTime) * currentSpeed);
                perfStartTime = realNow;
                virtualStartTime = currentVirtualTime;

                const currentVirtualDate = virtualDateStart + ((realNow - perfStartTime) * currentSpeed);
                virtualDateStart = currentVirtualDate;
            };

            ${state.debugLogging ? `console.log('[SpeedHack] Injected at ' + currentSpeed + 'x speed');` : ''}
        })();
        `;
    }

    function injectIntoWindow(targetWindow, speed, state) {
        try {
            if (!targetWindow || !targetWindow.document) return false;

            const script = targetWindow.document.createElement('script');
            script.textContent = createInjectionScript(speed, state);
            script.setAttribute('data-speedhack', 'true');

            const target = targetWindow.document.documentElement || targetWindow.document.head || targetWindow.document.body;
            if (target) {
                target.appendChild(script);
                return true;
            }
        } catch (e) {
            if (globalState.debugLogging) {
                console.warn('[SpeedHack] Injection failed:', e);
            }
        }
        return false;
    }

    function updateAllWindows() {
        // Update main window
        injectIntoWindow(window, globalSpeed, globalState);
        if (window.__updateSpeedHack) {
            window.__updateSpeedHack(globalSpeed, globalState);
        }

        // Update all iframes if enabled
        if (globalState.autoInjectFrames) {
            const frames = document.querySelectorAll('iframe');
            frames.forEach(frame => {
                try {
                    const frameWindow = frame.contentWindow;
                    if (frameWindow && frameWindow.document) {
                        injectIntoWindow(frameWindow, globalSpeed, globalState);
                        if (frameWindow.__updateSpeedHack) {
                            frameWindow.__updateSpeedHack(globalSpeed, globalState);
                        }
                    }
                } catch (e) {
                    // Cross-origin iframe, ignore
                }
            });
        }
    }

    function createUI() {
        if (uiElement) return;

        const ui = document.createElement('div');
        ui.id = 'speedhack-ui';
        ui.style.cssText = `
            position: fixed;
            top: 10px;
            right: 10px;
            padding: 16px;
            background: linear-gradient(135deg, rgba(0,0,0,0.95), rgba(30,30,30,0.95));
            color: #fff;
            z-index: 2147483647;
            border-radius: 12px;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            font-size: 13px;
            min-width: 280px;
            max-width: 320px;
            box-shadow: 0 8px 32px rgba(0,0,0,0.4);
            border: 1px solid rgba(255,255,255,0.15);
            backdrop-filter: blur(20px);
            user-select: none;
            transition: all 0.3s ease;
        `;

        ui.innerHTML = `
            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
                <strong style="color: #4CAF50; font-size: 14px;">⚡ Speed Hack Pro</strong>
                <div style="display: flex; gap: 8px;">
                    <button id="speedhack-settings" style="background: rgba(255,255,255,0.1); border: none; color: #fff; cursor: pointer; font-size: 14px; padding: 4px 8px; border-radius: 4px; transition: background 0.2s;">⚙️</button>
                    <button id="speedhack-minimize" style="background: rgba(255,255,255,0.1); border: none; color: #fff; cursor: pointer; font-size: 14px; padding: 4px 8px; border-radius: 4px; transition: background 0.2s;">−</button>
                </div>
            </div>
            <div id="speedhack-content">
                <div style="margin-bottom: 12px;">
                    <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 6px; font-size: 12px;">
                        <label style="display: flex; align-items: center; cursor: pointer; padding: 4px; border-radius: 4px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">
                            <input type="checkbox" id="toggle-perfnow" style="margin-right: 6px; accent-color: #4CAF50;"> performance.now()
                        </label>
                        <label style="display: flex; align-items: center; cursor: pointer; padding: 4px; border-radius: 4px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">
                            <input type="checkbox" id="toggle-datenow" style="margin-right: 6px; accent-color: #4CAF50;"> Date.now()
                        </label>
                        <label style="display: flex; align-items: center; cursor: pointer; padding: 4px; border-radius: 4px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">
                            <input type="checkbox" id="toggle-settimeout" style="margin-right: 6px; accent-color: #4CAF50;"> setTimeout
                        </label>
                        <label style="display: flex; align-items: center; cursor: pointer; padding: 4px; border-radius: 4px; transition: background 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">
                            <input type="checkbox" id="toggle-setinterval" style="margin-right: 6px; accent-color: #4CAF50;"> setInterval
                        </label>
                        <label style="display: flex; align-items: center; cursor: pointer; padding: 4px; border-radius: 4px; transition: background 0.2s; grid-column: span 2;" onmouseover="this.style.background='rgba(255,255,255,0.05)'" onmouseout="this.style.background='transparent'">
                            <input type="checkbox" id="toggle-raf" style="margin-right: 6px; accent-color: #4CAF50;"> requestAnimationFrame
                        </label>
                    </div>
                </div>
                <hr style="border: none; border-top: 1px solid rgba(255,255,255,0.2); margin: 12px 0;">

                <!-- Speed Control Section -->
                <div style="margin-bottom: 12px;">
                    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
                        <span style="font-weight: 500;">Speed Control:</span>
                        <span id="speed-display" style="color: #4CAF50; font-weight: bold; font-size: 14px;">${globalSpeed.toFixed(3)}x</span>
                    </div>

                    <!-- Custom Speed Input -->
                    <div style="display: flex; gap: 8px; margin-bottom: 8px;">
                        <input type="number" id="speed-input"
                               value="${globalSpeed}"
                               min="${CONFIG.minSpeed}"
                               max="${CONFIG.maxSpeed}"
                               step="any"
                               placeholder="Enter speed..."
                               style="flex: 1; padding: 6px 8px; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); color: #fff; border-radius: 6px; font-size: 12px; outline: none; transition: all 0.2s;"
                               onfocus="this.style.borderColor='#4CAF50'; this.style.background='rgba(76,175,80,0.1)'"
                               onblur="this.style.borderColor='rgba(255,255,255,0.2)'; this.style.background='rgba(255,255,255,0.1)'">
                        <button id="speed-apply" style="padding: 6px 12px; background: #4CAF50; border: none; color: white; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 500; transition: background 0.2s;" onmouseover="this.style.background='#45a049'" onmouseout="this.style.background='#4CAF50'">Apply</button>
                    </div>

                    <!-- Speed Slider -->
                    <input type="range" id="speed-slider"
                           min="${Math.log(CONFIG.minSpeed)}"
                           max="${Math.log(CONFIG.maxSpeed)}"
                           value="${Math.log(globalSpeed)}"
                           step="0.01"
                           style="width: 100%; margin-bottom: 8px; accent-color: #4CAF50;">

                    <!-- Quick Presets -->
                    <div style="display: flex; gap: 4px; margin-bottom: 8px;">
                        <button class="speed-preset" data-speed="0.1" style="flex: 1; padding: 4px; font-size: 10px; background: #333; border: 1px solid #555; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#444'" onmouseout="this.style.background='#333'">0.1x</button>
                        <button class="speed-preset" data-speed="0.25" style="flex: 1; padding: 4px; font-size: 10px; background: #333; border: 1px solid #555; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#444'" onmouseout="this.style.background='#333'">0.25x</button>
                        <button class="speed-preset" data-speed="0.5" style="flex: 1; padding: 4px; font-size: 10px; background: #333; border: 1px solid #555; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#444'" onmouseout="this.style.background='#333'">0.5x</button>
                        <button class="speed-preset" data-speed="1" style="flex: 1; padding: 4px; font-size: 10px; background: #4CAF50; border: 1px solid #4CAF50; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#45a049'" onmouseout="this.style.background='#4CAF50'">1x</button>
                        <button class="speed-preset" data-speed="2" style="flex: 1; padding: 4px; font-size: 10px; background: #333; border: 1px solid #555; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#444'" onmouseout="this.style.background='#333'">2x</button>
                        <button class="speed-preset" data-speed="5" style="flex: 1; padding: 4px; font-size: 10px; background: #333; border: 1px solid #555; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#444'" onmouseout="this.style.background='#333'">5x</button>
                        <button class="speed-preset" data-speed="10" style="flex: 1; padding: 4px; font-size: 10px; background: #333; border: 1px solid #555; color: #fff; border-radius: 4px; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.background='#444'" onmouseout="this.style.background='#333'">10x</button>
                    </div>

                    <!-- Advanced Controls -->
                    <div style="display: flex; gap: 4px;">
                        <button id="speed-save-preset" style="flex: 1; padding: 6px; font-size: 11px; background: rgba(255,193,7,0.8); border: 1px solid #FFC107; color: #000; border-radius: 4px; cursor: pointer; font-weight: 500; transition: all 0.2s;" onmouseover="this.style.background='#FFC107'" onmouseout="this.style.background='rgba(255,193,7,0.8)'">💾 Save</button>
                        <button id="speed-reset" style="flex: 1; padding: 6px; font-size: 11px; background: rgba(244,67,54,0.8); border: 1px solid #F44336; color: #fff; border-radius: 4px; cursor: pointer; font-weight: 500; transition: all 0.2s;" onmouseover="this.style.background='#F44336'" onmouseout="this.style.background='rgba(244,67,54,0.8)'">🔄 Reset</button>
                    </div>
                </div>

                <!-- Statistics -->
                <div id="speed-stats" style="font-size: 11px; color: #aaa; text-align: center; padding: 8px; background: rgba(255,255,255,0.05); border-radius: 6px;">
                    <div>Active: <span id="active-functions">0</span> functions</div>
                    <div>Uptime: <span id="uptime">0s</span></div>
                </div>
            </div>

            <!-- Advanced Settings Panel -->
            <div id="speedhack-settings-panel" style="display: none; margin-top: 12px; padding: 12px; background: rgba(255,255,255,0.05); border-radius: 8px; border-top: 1px solid rgba(255,255,255,0.1);">
                <div style="font-weight: 500; margin-bottom: 8px; color: #4CAF50;">⚙️ Advanced Settings</div>
                <div style="font-size: 12px;">
                    <label style="display: flex; align-items: center; margin-bottom: 6px; cursor: pointer;">
                        <input type="checkbox" id="auto-inject-frames" checked style="margin-right: 6px; accent-color: #4CAF50;"> Auto-inject into iframes
                    </label>
                    <label style="display: flex; align-items: center; margin-bottom: 6px; cursor: pointer;">
                        <input type="checkbox" id="debug-logging" style="margin-right: 6px; accent-color: #4CAF50;"> Debug logging
                    </label>
                    <label style="display: flex; align-items: center; margin-bottom: 8px; cursor: pointer;">
                        <input type="checkbox" id="persistent-ui" checked style="margin-right: 6px; accent-color: #4CAF50;"> Remember UI position
                    </label>
                    <button id="clear-settings" style="width: 100%; padding: 6px; background: rgba(244,67,54,0.2); border: 1px solid #F44336; color: #F44336; border-radius: 4px; cursor: pointer; font-size: 11px;">Clear All Settings</button>
                </div>
            </div>
        `;

        document.body.appendChild(ui);
        uiElement = ui;

        // Set initial checkbox states
        ui.querySelector('#toggle-perfnow').checked = globalState.perfNow;
        ui.querySelector('#toggle-datenow').checked = globalState.dateNow;
        ui.querySelector('#toggle-settimeout').checked = globalState.setTimeout;
        ui.querySelector('#toggle-setinterval').checked = globalState.setInterval;
        ui.querySelector('#toggle-raf').checked = globalState.raf;
        ui.querySelector('#auto-inject-frames').checked = globalState.autoInjectFrames;
        ui.querySelector('#debug-logging').checked = globalState.debugLogging;
        ui.querySelector('#persistent-ui').checked = globalState.persistentUI;

        // Event listeners
        const speedSlider = ui.querySelector('#speed-slider');
        const speedDisplay = ui.querySelector('#speed-display');
        const speedInput = ui.querySelector('#speed-input');
        const speedApply = ui.querySelector('#speed-apply');

        // Logarithmic slider handling
        function updateSpeedFromSlider() {
            const logValue = parseFloat(speedSlider.value);
            const actualSpeed = Math.exp(logValue);
            globalSpeed = Math.round(actualSpeed * 1000) / 1000; // Round to 3 decimal places
            speedDisplay.textContent = `${globalSpeed.toFixed(3)}x`;
            speedInput.value = globalSpeed;
            updateAllWindows();
            saveSettings();
            updatePresetHighlight();
        }

        function updateSliderFromSpeed(speed) {
            speedSlider.value = Math.log(speed);
            speedDisplay.textContent = `${speed.toFixed(3)}x`;
            speedInput.value = speed;
            updatePresetHighlight();
        }

        function updatePresetHighlight() {
            ui.querySelectorAll('.speed-preset').forEach(btn => {
                const presetSpeed = parseFloat(btn.dataset.speed);
                if (Math.abs(presetSpeed - globalSpeed) < 0.001) {
                    btn.style.background = '#4CAF50';
                    btn.style.borderColor = '#4CAF50';
                } else {
                    btn.style.background = '#333';
                    btn.style.borderColor = '#555';
                }
            });
        }

        // Speed input validation and application
        function applyCustomSpeed() {
            let inputValue = parseFloat(speedInput.value);
            if (isNaN(inputValue) || inputValue <= 0) {
                speedInput.value = globalSpeed;
                speedInput.style.borderColor = '#F44336';
                setTimeout(() => {
                    speedInput.style.borderColor = 'rgba(255,255,255,0.2)';
                }, 1000);
                return;
            }

            inputValue = Math.max(CONFIG.minSpeed, Math.min(CONFIG.maxSpeed, inputValue));
            globalSpeed = inputValue;
            updateSliderFromSpeed(globalSpeed);
            updateAllWindows();
            saveSettings();

            // Visual feedback
            speedApply.textContent = '✓';
            speedApply.style.background = '#4CAF50';
            setTimeout(() => {
                speedApply.textContent = 'Apply';
                speedApply.style.background = '#4CAF50';
            }, 500);
        }

        speedSlider.addEventListener('input', updateSpeedFromSlider);
        speedApply.addEventListener('click', applyCustomSpeed);
        speedInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') {
                applyCustomSpeed();
                speedInput.blur();
            }
        });

        // Update statistics
        function updateStats() {
            const activeFunctions = Object.values(globalState).filter((val, key) =>
                typeof val === 'boolean' && val && key < 5
            ).length;
            const uptime = Math.floor((Date.now() - startTime) / 1000);

            const statsElement = ui.querySelector('#active-functions');
            const uptimeElement = ui.querySelector('#uptime');

            if (statsElement) statsElement.textContent = activeFunctions;
            if (uptimeElement) {
                if (uptime < 60) {
                    uptimeElement.textContent = `${uptime}s`;
                } else if (uptime < 3600) {
                    uptimeElement.textContent = `${Math.floor(uptime/60)}m ${uptime%60}s`;
                } else {
                    uptimeElement.textContent = `${Math.floor(uptime/3600)}h ${Math.floor((uptime%3600)/60)}m`;
                }
            }
        }

        setInterval(updateStats, 1000);
        updateStats();

        // Checkbox listeners
        ui.querySelector('#toggle-perfnow').addEventListener('change', (e) => {
            globalState.perfNow = e.target.checked;
            updateAllWindows();
            saveSettings();
        });

        ui.querySelector('#toggle-datenow').addEventListener('change', (e) => {
            globalState.dateNow = e.target.checked;
            updateAllWindows();
            saveSettings();
        });

        ui.querySelector('#toggle-settimeout').addEventListener('change', (e) => {
            globalState.setTimeout = e.target.checked;
            updateAllWindows();
            saveSettings();
        });

        ui.querySelector('#toggle-setinterval').addEventListener('change', (e) => {
            globalState.setInterval = e.target.checked;
            updateAllWindows();
            saveSettings();
        });

        ui.querySelector('#toggle-raf').addEventListener('change', (e) => {
            globalState.raf = e.target.checked;
            updateAllWindows();
            saveSettings();
        });

        // Advanced settings listeners
        ui.querySelector('#auto-inject-frames').addEventListener('change', (e) => {
            globalState.autoInjectFrames = e.target.checked;
            saveSettings();
        });

        ui.querySelector('#debug-logging').addEventListener('change', (e) => {
            globalState.debugLogging = e.target.checked;
            saveSettings();
        });

        ui.querySelector('#persistent-ui').addEventListener('change', (e) => {
            globalState.persistentUI = e.target.checked;
            saveSettings();
        });

        // Preset buttons with improved handling
        ui.querySelectorAll('.speed-preset').forEach(button => {
            button.addEventListener('click', () => {
                const speed = parseFloat(button.dataset.speed);
                globalSpeed = speed;
                updateSliderFromSpeed(globalSpeed);
                updateAllWindows();
                saveSettings();
            });
        });

        // Advanced controls
        ui.querySelector('#speed-save-preset').addEventListener('click', () => {
            const presetName = prompt(`Save current speed (${globalSpeed.toFixed(3)}x) as preset:`, `${globalSpeed.toFixed(3)}x`);
            if (presetName) {
                if (!globalState.customPresets) globalState.customPresets = [];
                globalState.customPresets.push({ name: presetName, speed: globalSpeed });
                saveSettings();

                // Show confirmation
                const btn = ui.querySelector('#speed-save-preset');
                const originalText = btn.textContent;
                btn.textContent = '✓ Saved';
                setTimeout(() => btn.textContent = originalText, 1000);
            }
        });

        ui.querySelector('#speed-reset').addEventListener('click', () => {
            if (confirm('Reset speed to 1.0x?')) {
                globalSpeed = 1.0;
                updateSliderFromSpeed(globalSpeed);
                updateAllWindows();
                saveSettings();
            }
        });

        // Settings panel toggle
        ui.querySelector('#speedhack-settings').addEventListener('click', () => {
            const panel = ui.querySelector('#speedhack-settings-panel');
            panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
        });

        // Settings panel controls
        ui.querySelector('#clear-settings').addEventListener('click', () => {
            if (confirm('Clear all saved settings? This will reset everything to defaults.')) {
                localStorage.removeItem(STORAGE_KEY);
                location.reload();
            }
        });

        // Minimize button
        ui.querySelector('#speedhack-minimize').addEventListener('click', () => {
            const content = ui.querySelector('#speedhack-content');
            const button = ui.querySelector('#speedhack-minimize');
            const settingsPanel = ui.querySelector('#speedhack-settings-panel');

            if (content.style.display === 'none') {
                content.style.display = 'block';
                button.textContent = '−';
                ui.style.minWidth = '280px';
            } else {
                content.style.display = 'none';
                settingsPanel.style.display = 'none';
                button.textContent = '+';
                ui.style.minWidth = 'auto';
                ui.style.width = 'auto';
            }
        });

        // Make draggable with improved positioning
        let isDragging = false;
        let dragOffset = { x: 0, y: 0 };

        ui.addEventListener('mousedown', (e) => {
            if (!e.target.matches('input, button, label')) {
                isDragging = true;
                dragOffset.x = e.clientX - ui.offsetLeft;
                dragOffset.y = e.clientY - ui.offsetTop;
                ui.style.cursor = 'grabbing';
                ui.style.transition = 'none';
            }
        });

        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                const newX = Math.max(0, Math.min(window.innerWidth - ui.offsetWidth, e.clientX - dragOffset.x));
                const newY = Math.max(0, Math.min(window.innerHeight - ui.offsetHeight, e.clientY - dragOffset.y));

                ui.style.left = newX + 'px';
                ui.style.top = newY + 'px';
                ui.style.right = 'auto';
                ui.style.bottom = 'auto';
            }
        });

        document.addEventListener('mouseup', () => {
            if (isDragging) {
                isDragging = false;
                ui.style.cursor = 'default';
                ui.style.transition = 'all 0.3s ease';
                if (globalState.persistentUI) {
                    saveSettings();
                }
            }
        });

        // Keyboard shortcuts
        document.addEventListener('keydown', (e) => {
            if (e.ctrlKey && e.shiftKey) {
                switch(e.code) {
                    case 'KeyS':
                        e.preventDefault();
                        const panel = ui.querySelector('#speedhack-settings-panel');
                        panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
                        break;
                    case 'KeyR':
                        e.preventDefault();
                        globalSpeed = 1.0;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'KeyH':
                        e.preventDefault();
                        const content = ui.querySelector('#speedhack-content');
                        const button = ui.querySelector('#speedhack-minimize');
                        if (content.style.display === 'none') {
                            content.style.display = 'block';
                            button.textContent = '−';
                        } else {
                            content.style.display = 'none';
                            button.textContent = '+';
                        }
                        break;
                    case 'Digit1':
                        e.preventDefault();
                        globalSpeed = 0.1;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'Digit2':
                        e.preventDefault();
                        globalSpeed = 0.25;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'Digit3':
                        e.preventDefault();
                        globalSpeed = 0.5;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'Digit4':
                        e.preventDefault();
                        globalSpeed = 1.0;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'Digit5':
                        e.preventDefault();
                        globalSpeed = 2.0;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'Digit6':
                        e.preventDefault();
                        globalSpeed = 5.0;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                    case 'Digit7':
                        e.preventDefault();
                        globalSpeed = 10.0;
                        updateSliderFromSpeed(globalSpeed);
                        updateAllWindows();
                        saveSettings();
                        break;
                }
            }
        });

        // Load saved UI position if persistent UI is enabled
        const savedSettings = loadSettings();
        if (globalState.persistentUI && savedSettings && savedSettings.uiPosition) {
            if (savedSettings.uiPosition.left && savedSettings.uiPosition.top) {
                ui.style.left = savedSettings.uiPosition.left;
                ui.style.top = savedSettings.uiPosition.top;
                ui.style.right = 'auto';
            }
        }

        // Initial slider position and preset highlight
        updateSliderFromSpeed(globalSpeed);

        // Add mouse wheel support for speed adjustment
        ui.addEventListener('wheel', (e) => {
            if (e.ctrlKey) {
                e.preventDefault();
                const delta = e.deltaY > 0 ? 0.9 : 1.1;
                globalSpeed = Math.max(CONFIG.minSpeed, Math.min(CONFIG.maxSpeed, globalSpeed * delta));
                globalSpeed = Math.round(globalSpeed * 1000) / 1000;
                updateSliderFromSpeed(globalSpeed);
                updateAllWindows();
                saveSettings();
            }
        });

        // Add double-click to reset speed
        ui.addEventListener('dblclick', (e) => {
            if (e.target === speedDisplay) {
                globalSpeed = 1.0;
                updateSliderFromSpeed(globalSpeed);
                updateAllWindows();
                saveSettings();
            }
        });

        // Add context menu for quick actions
        ui.addEventListener('contextmenu', (e) => {
            e.preventDefault();
            const contextMenu = document.createElement('div');
            contextMenu.style.cssText = `
                position: fixed;
                left: ${e.clientX}px;
                top: ${e.clientY}px;
                background: rgba(0,0,0,0.9);
                border: 1px solid rgba(255,255,255,0.2);
                border-radius: 6px;
                padding: 8px 0;
                z-index: 2147483648;
                font-size: 12px;
                min-width: 150px;
                backdrop-filter: blur(10px);
            `;

            const menuItems = [
                { text: 'Reset to 1.0x', action: () => { globalSpeed = 1.0; updateSliderFromSpeed(globalSpeed); updateAllWindows(); saveSettings(); }},
                { text: 'Toggle All Functions', action: () => {
                    const allEnabled = Object.values(globalState).slice(0, 5).every(v => v);
                    ['perfNow', 'dateNow', 'setTimeout', 'setInterval', 'raf'].forEach(key => {
                        globalState[key] = !allEnabled;
                        ui.querySelector(`#toggle-${key.toLowerCase()}`).checked = !allEnabled;
                    });
                    updateAllWindows(); saveSettings();
                }},
                { text: 'Copy Current Speed', action: () => {
                    navigator.clipboard.writeText(globalSpeed.toString());
                    speedDisplay.textContent = 'Copied!';
                    setTimeout(() => speedDisplay.textContent = `${globalSpeed.toFixed(3)}x`, 1000);
                }},
                { text: 'Export Settings', action: () => {
                    const settings = JSON.stringify({ speed: globalSpeed, state: globalState }, null, 2);
                    navigator.clipboard.writeText(settings);
                    alert('Settings copied to clipboard!');
                }}
            ];

            menuItems.forEach(item => {
                const menuItem = document.createElement('div');
                menuItem.textContent = item.text;
                menuItem.style.cssText = `
                    padding: 6px 12px;
                    cursor: pointer;
                    color: #fff;
                    transition: background 0.2s;
                `;
                menuItem.addEventListener('mouseover', () => menuItem.style.background = 'rgba(255,255,255,0.1)');
                menuItem.addEventListener('mouseout', () => menuItem.style.background = 'transparent');
                menuItem.addEventListener('click', () => {
                    item.action();
                    document.body.removeChild(contextMenu);
                });
                contextMenu.appendChild(menuItem);
            });

            document.body.appendChild(contextMenu);

            // Remove context menu when clicking elsewhere
            const removeMenu = (e) => {
                if (!contextMenu.contains(e.target)) {
                    document.body.removeChild(contextMenu);
                    document.removeEventListener('click', removeMenu);
                }
            };
            setTimeout(() => document.addEventListener('click', removeMenu), 100);
        });

        // Add notification system
        function showNotification(message, type = 'info') {
            const notification = document.createElement('div');
            notification.style.cssText = `
                position: fixed;
                top: 20px;
                left: 50%;
                transform: translateX(-50%);
                background: ${type === 'error' ? 'rgba(244,67,54,0.9)' : 'rgba(76,175,80,0.9)'};
                color: white;
                padding: 8px 16px;
                border-radius: 6px;
                font-size: 12px;
                z-index: 2147483648;
                backdrop-filter: blur(10px);
                animation: slideDown 0.3s ease;
            `;

            const style = document.createElement('style');
            style.textContent = `
                @keyframes slideDown {
                    from { opacity: 0; transform: translateX(-50%) translateY(-20px); }
                    to { opacity: 1; transform: translateX(-50%) translateY(0); }
                }
            `;
            document.head.appendChild(style);

            notification.textContent = message;
            document.body.appendChild(notification);

            setTimeout(() => {
                document.body.removeChild(notification);
                document.head.removeChild(style);
            }, 3000);
        }

        // Add success notification for settings save/load
        const originalSaveSettings = saveSettings;
        window.speedHackShowNotification = showNotification;
    }

    function initialize() {
        if (isInitialized) return;
        isInitialized = true;

        loadSettings();

        // Initial injection
        updateAllWindows();

        // Create UI when DOM is ready
        if (document.body) {
            createUI();
        } else {
            const observer = new MutationObserver((mutations, obs) => {
                if (document.body) {
                    createUI();
                    obs.disconnect();
                }
            });
            observer.observe(document, { childList: true, subtree: true });
        }

        // Monitor for new iframes
        if (globalState.autoInjectFrames) {
            const frameObserver = new MutationObserver((mutations) => {
                mutations.forEach((mutation) => {
                    mutation.addedNodes.forEach((node) => {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            const iframes = node.tagName === 'IFRAME' ? [node] : node.querySelectorAll('iframe');
                            iframes.forEach((iframe) => {
                                iframe.addEventListener('load', () => {
                                    setTimeout(() => updateAllWindows(), 100);
                                });
                            });
                        }
                    });
                });
            });

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

        // Add window resize handler for UI positioning
        window.addEventListener('resize', () => {
            if (uiElement && globalState.persistentUI) {
                const rect = uiElement.getBoundingClientRect();
                if (rect.right > window.innerWidth) {
                    uiElement.style.left = (window.innerWidth - uiElement.offsetWidth - 10) + 'px';
                }
                if (rect.bottom > window.innerHeight) {
                    uiElement.style.top = (window.innerHeight - uiElement.offsetHeight - 10) + 'px';
                }
            }
        });

        if (globalState.debugLogging) {
            console.log('[SpeedHack] Pro Enhanced version initialized successfully');
        }
    }

    // Initialize immediately if possible, otherwise wait for DOM
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        setTimeout(initialize, 100);
    }

})();