Greasy Fork

来自缓存

Greasy Fork is available in English.

【网页防挂机】 网页可见性管理

在指定域名作用范围内维持页面为可见/活跃状态。内置配置面板,支持动态维护作用域列表、快捷键操作与持久化存储;未启用域名不执行任何逻辑。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         【网页防挂机】 网页可见性管理
// @namespace    https://github.com/realSilasYang
// @version      2026-04-14
// @description  在指定域名作用范围内维持页面为可见/活跃状态。内置配置面板,支持动态维护作用域列表、快捷键操作与持久化存储;未启用域名不执行任何逻辑。
// @author       阳熙来
// @match        *://*/*
// @run-at       document-start
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @icon         data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJpY29uIiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB2ZXJzaW9uPSIxLjEiIGRhdGEtZGFya3JlYWRlci1pbmxpbmUtZmlsbD0iIiBkYXRhLXNwbS1hbmNob3ItaWQ9ImEzMTN4LnNlYXJjaF9pbmRleC4wLmkxLjYxYjMzYTgxc0dDRFVlIiB3aWR0aD0iMjU2IiBoZWlnaHQ9IjI1NiI+CiAgPHBhdGggZD0iTTUxNS4wNTE1MiA1MDUuNTIzMmMtMjEuNTA0LTEuNTM2LTQwLjQ0OCAxNC4zMzYtNDEuOTg0IDM1Ljg0IDAgMTMuMzEyIDcuNjggMjUuNiAxOS40NTYgMzIuMjU2djQ5LjE1MmMwIDEwLjc1MiA4LjcwNCAxOS40NTYgMTkuNDU2IDE5LjQ1NiAxMC43NTIgMCAxOS40NTYtOC43MDQgMTkuNDU2LTE5LjQ1NnYtNDkuMTUyYzExLjc3Ni02LjY1NiAxOC45NDQtMTguOTQ0IDE5LjQ1Ni0zMi4yNTYtMS41MzYtMTguOTQ0LTE2Ljg5Ni0zNC4zMDQtMzUuODQtMzUuODR6TTYwOS4yNTk1MiAzOTIuMzcxMmMtMi41Ni01MC4xNzYtNDIuNDk2LTkwLjExMi05Mi42NzItOTIuNjcyLTUzLjc2LTIuNTYtOTguODE2IDM4LjkxMi0xMDEuMzc2IDkyLjY3MnY0NS4wNTZoMTk0LjA0OHYtNDUuMDU2eiIgZmlsbD0iI2Y3NmY1MyIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDogdmFyKC0tZGFya3JlYWRlci1iYWNrZ3JvdW5kLWY3NmY1MywgIzk0MmUxOSk7IiBkYXRhLWRhcmtyZWFkZXItaW5saW5lLWZpbGw9IiIgZGF0YS1zcG0tYW5jaG9yLWlkPSJhMzEzeC5zZWFyY2hfaW5kZXguMC5pMC42MWIzM2E4MXNHQ0RVZSIgY2xhc3M9InNlbGVjdGVkIj48L3BhdGg+CiAgPHBhdGggZD0iTTk2Ni4xMjM1MiAyOTEuNTA3MmMtMS4wMjQtNy4xNjgtNS4xMi0xNC4zMzYtMTAuNzUyLTE4Ljk0NGwtMjczLjkyLTI0Mi4xNzZjLTkuNzI4LTguMTkyLTIyLjAxNi0xMS4yNjQtMzQuMzA0LTguNzA0aC00NTEuMDcyYy03Mi43MDQtNC4wOTYtMTM1LjY4IDUxLjItMTQwLjI4OCAxMjQuNDE2djY4My4wMDhjNC42MDggNzIuNzA0IDY3LjU4NCAxMjguNTEyIDE0MC4yODggMTI0LjQxNmg2MzEuODA4YzcyLjcwNCA0LjA5NiAxMzUuNjgtNTEuMiAxNDAuMjg4LTEyNC40MTZ2LTUyOC4zODRjMC0zLjA3Mi0wLjUxMi02LjY1Ni0yLjA0OC05LjIxNnogbS02NDcuMTY4IDE4OS45NTJjMC41MTItMjUuMDg4IDIwLjk5Mi00NC41NDQgNDYuMDgtNDQuMDMyaDExLjI2NHYtNDMuNTItMi41NmMxLjUzNi03My4yMTYgNjIuNDY0LTEzMS41ODQgMTM1LjY4LTEzMC4wNDhoMi41NmM3My4yMTYgMCAxMzMuMTIgNTkuMzkyIDEzMy4xMiAxMzMuMTJ2NDMuNTJoMTEuMjY0YzI1LjA4OC0wLjUxMiA0NS41NjggMTguOTQ0IDQ2LjA4IDQ0LjAzMnYxODYuODhjLTAuNTEyIDI1LjA4OC0yMC45OTIgNDQuNTQ0LTQ2LjA4IDQ0LjAzMmgtMjk0LjRjLTI1LjA4OCAwLjUxMi00NS41NjgtMTguOTQ0LTQ2LjA4LTQ0LjAzMnYtMTg3LjM5MnogbTM4Ny41ODQgMzQ1LjZoLTM4OS42MzJjLTExLjI2NCAwLTIwLjQ4LTkuMjE2LTIwLjQ4LTIwLjQ4czkuMjE2LTIwLjQ4IDIwLjQ4LTIwLjQ4aDM4OS42MzJjMTEuMjY0IDAgMjAuNDggOS4yMTYgMjAuNDggMjAuNDhzLTguNzA0IDIwLjQ4LTIwLjQ4IDIwLjQ4eiIgZmlsbD0iIzU4NUU2NiIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDogdmFyKC0tZGFya3JlYWRlci1iYWNrZ3JvdW5kLTU4NWU2NiwgIzUwNTU1Nyk7IiBkYXRhLWRhcmtyZWFkZXItaW5saW5lLWZpbGw9IiIgZGF0YS1zcG0tYW5jaG9yLWlkPSJhMzEzeC5zZWFyY2hfaW5kZXguMC5pMi42MWIzM2E4MXNHQ0RVZSIgY2xhc3M9IiI+PC9wYXRoPgo8L3N2Zz4K
// @license      GNU GPLv3
// ==/UserScript==

(function() {
    'use strict';

    // === 1. 数据读取与判断 ===
    const STORE_KEY = 'anti_idle_domains';
    const currentHost = window.location.hostname;
    let enabledDomains = GM_getValue(STORE_KEY, []);

    // 极速拦截:不在白名单内直接停止后续逻辑,实现“0资源占用”
    const isEnabled = enabledDomains.includes(currentHost);

    // 注册油猴菜单
    GM_registerMenuCommand('⚙️ 生效域名管理', showManagerPanel);

    // 若未启用,直接退出
    if (!isEnabled) return;


    // === 2. 核心突破逻辑 (仅在白名单域名执行) ===
    const stopEvent = function(e) {
        e.stopImmediatePropagation();
    };

    window.addEventListener('blur', stopEvent, true);
    document.addEventListener('visibilitychange', stopEvent, true);
    document.addEventListener('webkitvisibilitychange', stopEvent, true);

    const defineProp = (obj, prop, val) => {
        try {
            Object.defineProperty(obj, prop, {
                get: function() { return val; },
                configurable: true
            });
        } catch (e) {} // 静默失败,防止污染控制台
    };

    defineProp(document, 'visibilityState', 'visible');
    defineProp(document, 'hidden', false);
    defineProp(document, 'webkitVisibilityState', 'visible');
    defineProp(document, 'webkitHidden', false);

    console.log(`[网页防挂机] 当前域名已在列表内,防挂机功能已激活: ${currentHost}`);


    // === 3. 管理面板 UI 与逻辑 ===
    function showManagerPanel() {
        if (document.getElementById('anti-idle-manager-mask')) return;

        let currentDomains = GM_getValue(STORE_KEY, []);
        let isCurrentEnabled = currentDomains.includes(currentHost);

        // 注入面板 HTML
        const mask = document.createElement('div');
        mask.id = 'anti-idle-manager-mask';
        mask.style.cssText = `
            position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
            background: rgba(0, 0, 0, 0.5); z-index: 999999999;
            display: flex; justify-content: center; align-items: center;
            font-family: system-ui, -apple-system, sans-serif;
            backdrop-filter: blur(2px);
        `;

        mask.innerHTML = `
            <div style="background: #fff; padding: 24px; border-radius: 10px; width: 420px; max-width: 90%; color: #333; box-shadow: 0 10px 30px rgba(0,0,0,0.2);">
                <h3 style="margin: 0 0 16px 0; font-size: 18px; border-bottom: 1px solid #eaeaea; padding-bottom: 12px; display: flex; justify-content: space-between; align-items: center;">
                    <span>🛡️ 防挂机域名列表</span>
                </h3>
                <div style="margin-bottom: 16px; font-size: 14px; display: flex; align-items: center; justify-content: space-between;">
                    <div>当前域名: <strong style="color:#0056b3;">${currentHost}</strong></div>
                    <button id="anti-idle-toggle-btn" style="padding: 6px 10px; cursor: pointer; border: 1px solid transparent; background: ${isCurrentEnabled ? '#ffebee' : '#e8f5e9'}; color: ${isCurrentEnabled ? '#c62828' : '#2e7d32'}; border-radius: 6px; font-weight: 500; transition: all 0.2s;">
                        ${isCurrentEnabled ? '🚫 移出列表' : '✅ 加入列表'}
                    </button>
                </div>
                <div style="margin-bottom: 8px; font-size: 13px; color: #555; font-weight: bold;">
                    生效域名列表 (每行一个,支持手动编辑):
                </div>
                <textarea id="anti-idle-textarea" style="width: 100%; height: 160px; padding: 10px; box-sizing: border-box; border: 1px solid #ddd; border-radius: 6px; font-family: 'Consolas', monospace; font-size: 13px; resize: vertical; outline: none;" placeholder="在此输入需要生效的域名..."></textarea>
                <div style="margin-top: 20px; display: flex; justify-content: flex-end; gap: 10px;">
                    <button id="anti-idle-close-btn" style="padding: 8px 16px; cursor: pointer; border: 1px solid #ddd; background: #f8f9fa; border-radius: 6px; color: #444; font-size: 13px; transition: background 0.2s;">取消 (Esc)</button>
                    <button id="anti-idle-save-btn" style="padding: 8px 16px; cursor: pointer; border: none; background: #007bff; color: white; border-radius: 6px; font-size: 13px; font-weight: 500; transition: background 0.2s;">💾 保存并刷新 (Ctrl+S)</button>
                </div>
            </div>
        `;

        document.body.appendChild(mask);

        const textarea = document.getElementById('anti-idle-textarea');
        textarea.value = currentDomains.join('\n');
        // 自动聚焦文本框
        textarea.focus();

        // 统一的关闭函数
        const closePanel = () => {
            mask.remove();
            document.removeEventListener('keydown', handleKeydown);
        };

        // 统一的保存函数
        const saveAndReload = () => {
            let newList = textarea.value.split('\n').map(s => s.trim()).filter(s => s);
            newList = [...new Set(newList)]; // 数组去重
            GM_setValue(STORE_KEY, newList);
            window.location.reload();
        };

        // 事件绑定:切换当前域名状态
        document.getElementById('anti-idle-toggle-btn').addEventListener('click', function() {
            let list = textarea.value.split('\n').map(s => s.trim()).filter(s => s);
            if (isCurrentEnabled) {
                list = list.filter(d => d !== currentHost);
                this.innerHTML = '✅ 加入列表';
                this.style.background = '#e8f5e9';
                this.style.color = '#2e7d32';
            } else {
                if (!list.includes(currentHost)) list.push(currentHost);
                this.innerHTML = '🚫 移出列表';
                this.style.background = '#ffebee';
                this.style.color = '#c62828';
            }
            isCurrentEnabled = !isCurrentEnabled;
            textarea.value = list.join('\n');
        });

        // 事件绑定:鼠标点击按钮或遮罩层关闭
        document.getElementById('anti-idle-close-btn').addEventListener('click', closePanel);
        mask.addEventListener('click', (e) => { if (e.target === mask) closePanel(); });

        // 事件绑定:鼠标点击保存
        document.getElementById('anti-idle-save-btn').addEventListener('click', saveAndReload);

        // 事件绑定:全局快捷键监听
        const handleKeydown = (e) => {
            // Esc 退出
            if (e.key === 'Escape') {
                e.preventDefault();
                closePanel();
            }
            // Ctrl+S 或 Cmd+S 保存
            else if ((e.ctrlKey || e.metaKey) && e.key === 's') {
                e.preventDefault();
                saveAndReload();
            }
        };
        document.addEventListener('keydown', handleKeydown);
    }

})();