Greasy Fork

来自缓存

Greasy Fork is available in English.

NetChk_Triangle_IronLock v37 beta

深度修复隐藏逻辑,确保点击隐藏后立即生效并持久化

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         NetChk_Triangle_IronLock v37 beta
// @version      37.5.31.b3
// @namespace    http://greasyfork.icu/zh-CN/users/1593463-nander
// @license      CC BY-NC-SA 4.0
// @description  深度修复隐藏逻辑,确保点击隐藏后立即生效并持久化
// @author       Gemini
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_openInTab
// @grant        GM_registerMenuCommand
// @connect      *
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    const COLORS = {
        bg: '#1a1a1a',
        border: '#333333',
        textMain: '#ffffff',
        text80: '#cccccc',
        text60: '#999999',
        text40: '#666666',
        vscBlue: '#005a9e',
        nckCN: '#4ade80',
        nckGL: '#60a5fa',
        nckERR: '#f87171',
        nckWarn: '#fbbf24'
    };

    const STYLES = `
        #nck-main {
            position: fixed !important; z-index: 2147483647 !important;
            background: ${COLORS.bg} !important; border: 1px solid ${COLORS.border} !important;
            border-radius: 2px !important; height: 24px !important;
            display: flex !important; align-items: stretch !important;
            box-sizing: border-box !important; cursor: default !important;
            user-select: none !important; font-family: "Cascadia Code", Consolas, monospace !important;
            box-shadow: 0 2px 8px rgba(0,0,0,0.5) !important;
            transition: opacity 0.3s, background 0.3s;
        }

        #nck-panel-container {
            position: absolute !important; display: none !important;
            left: 50% !important; transform: translateX(-50%) !important; width: 210px !important;
        }
        #nck-panel-container::before { content: ''; position: absolute; left: 0; right: 0; height: 10px; }

        .dir-down { top: 24px !important; padding-top: 5px !important; }
        .dir-down::before { top: -10px; }
        .dir-up { bottom: 24px !important; padding-bottom: 5px !important; }
        .dir-up::before { bottom: -10px; }

        #nck-main:hover #nck-panel-container, #nck-main.keep-open #nck-panel-container { display: block !important; }

        #nck-panel {
            background: ${COLORS.bg} !important; border: 1px solid ${COLORS.border} !important;
            padding: 6px !important; box-shadow: 0 4px 15px rgba(0,0,0,0.7) !important;
        }

        .panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px; border-bottom: 1px solid #333; padding-bottom: 3px; }
        .panel-ver { font-size: 8px; color: ${COLORS.text40}; font-weight: bold; }
        .ip-link-btn { cursor: pointer; color: ${COLORS.text60}; font-size: 8px; padding: 0 4px; border: 1px solid #444; background: #252526; }

        .ip-row { background: #222; padding: 4px 6px; margin-bottom: 3px; border: 1px solid #2d2d2d; }
        .ip-row-info { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2px; }
        .ip-label { font-size: 8px; color: ${COLORS.text40}; font-weight: bold; }
        .ip-label b { color: ${COLORS.text60}; margin-left: 5px; font-weight: normal; }
        .ip-ms { font-size: 9px; font-weight: 900; opacity: 0.9;}
        .ip-val {
            color: ${COLORS.text80}; font-size: 10px; font-family: "Cascadia Code", monospace;
            word-break: break-all; line-height: 1.2; display: block;
            border-top: 1px solid #2d2d2d; padding-top: 3px; margin-top: 2px;
        }

        .ms-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 3px; margin: 5px 0; }
        .ms-node { font-size: 9px; text-align: center; color: ${COLORS.text40}; background: #252526; padding: 3px 0; border: 1px solid #333; }
        .ms-node.active { color: ${COLORS.text80}; border-color: #444; }
        .ms-node b { display: block; font-size: 7px; opacity: 0.6; margin-bottom: 1px; }

        .btn-group { display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px; margin-top: 5px; }
        .t-btn { background: #2a2a2a; color: ${COLORS.text60}; font-size: 8px; padding: 4px 0; text-align: center; cursor: pointer; border: 1px solid #333; transition: 0.1s; }
        .t-btn:hover { background: #333; color: #fff; }
        .t-btn.active { background: ${COLORS.vscBlue} !important; color: #fff !important; border-color: #007acc !important; }

        #nck-trigger { display: flex; align-items: center; padding: 0 8px; flex-grow: 1; cursor: pointer; gap: 6px; }
        .info-code { color: #fff; font-size: 10px; font-weight: bold; }
        .info-ms { font-size: 10px; font-weight: 900;min-width: 30px; }
        .ms-cn { color: ${COLORS.nckCN}; }
        .ms-gl { color: ${COLORS.nckGL}; }

        .nck-led { width: 4px; height: 4px; border-radius: 1px; background: #333; transition: 0.3s; }
        .led-checking { background: ${COLORS.nckWarn} !important; box-shadow: 0 0 4px ${COLORS.nckWarn}; }
        .led-ok-cn { background: ${COLORS.nckCN} !important; box-shadow: 0 0 3px ${COLORS.nckCN}; }
        .led-ok-gl { background: ${COLORS.nckGL} !important; box-shadow: 0 0 3px ${COLORS.nckGL}; }
        .led-err { background: ${COLORS.nckERR} !important; }

        #nck-bay { width: 24px; border-left: 1px solid ${COLORS.border}; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: 0.2s; }
        #nck-bay:hover { background: #2a2a2a; }
        .bay-play { width: 0; height: 0; border-top: 4px solid transparent; border-bottom: 4px solid transparent; border-left: 6px solid #555; transition: 0.2s; }

        .ctrl-active .bay-play { border-left-color: ${COLORS.nckCN} !important; }

        @keyframes success-flash { 0% { background: #1a1a1a; } 50% { background: #1a4a2a; } 100% { background: #1a1a1a; } }
        .state-success { animation: success-flash 0.4s ease; }
        .state-success .bay-play { border-left-color: ${COLORS.nckCN} !important; transform: scale(1.2); }

        @keyframes error-shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-3px); } 75% { transform: translateX(3px); } }
        .state-error { animation: error-shake 0.2s 2; border-color: ${COLORS.nckERR} !important; }
        .state-error .bay-play { border-left-color: ${COLORS.nckERR} !important; }

        .hidden { display: none !important; }
    `;

    let state = {
        isProcessing: false,
        nodes: {
            cn: { ip: '...', ms: '--', state: 'init' },
            gl: { ip: '...', ms: '--', state: 'init' },
            code: 'VSC'
        },
        targets: [
            { id: 'BD', url: 'https://www.baidu.com/favicon.ico' },
            { id: 'GH', url: 'https://github.com/favicon.ico' },
            { id: 'CF', url: 'https://1.1.1.1/cdn-cgi/trace' }
        ],
        conf: {
            visible: GM_getValue('nck_v378_vis', true),
            matrix: GM_getValue('nck_v378_mtx', true),
            longShow: GM_getValue('nck_v378_long', false),
            locked: GM_getValue('nck_v378_lock', false),
            bindCtrl: GM_getValue('nck_v378_ctrl', true),
            bayOn: GM_getValue('nck_v378_bay', true),
            pingShow: GM_getValue('nck_v378_ping', true),
            lang: GM_getValue('nck_v378_lang', 'zh')
        },
        langLib: {
            zh: { mtx:'矩阵', bay:'弹仓', keep:'驻留', lock:'锁定', ctrl:'劫持', lang:'中文', hide:'隐藏', ping:'数值' },
            en: { mtx:'MTX', bay:'BAY', keep:'KEEP', lock:'LOCK', ctrl:'CTRL', lang:'EN', hide:'HIDE', ping:'PING' }
        }
    };

    function syncUI() {
        const box = document.getElementById('nck-main');
        if (!box) return;

        // 深度修复:强制设置 display 属性,确保可见性受控
        if (state.conf.visible) {
            box.style.setProperty('display', 'flex', 'important');
        } else {
            box.style.setProperty('display', 'none', 'important');
        }

        box.classList.toggle('keep-open', state.conf.longShow);

        const bay = document.getElementById('nck-bay');
        if (bay) {
            bay.classList.toggle('hidden', !state.conf.bayOn);
            bay.classList.toggle('ctrl-active', state.conf.bindCtrl);
        }

        // 刷新内容
        const msCN =  state.nodes.cn.ms ;
        const msGL =  state.nodes.gl.ms  ;
        // 在 syncUI 函数内部找到 infoBox 渲染逻辑并替换:

        const infoBox = document.getElementById('nck-info-box');
        if (infoBox) {
            // 确保即使在加载中,也有占位符或正确的数值显示
            const displayCN = state.conf.pingShow ? (state.nodes.cn.ms || '--') : '';
            const displayGL = state.conf.pingShow ? (state.nodes.gl.ms || '--') : '';

            infoBox.innerHTML = `
        <span class="info-code">${state.nodes.code || 'VSC'}</span>
        <span class="info-ms ms-cn">${displayCN}</span>
        <span class="info-ms ms-gl">${displayGL}</span>`;
        }

        // LED
        const ledCN = document.getElementById('led-cn');
        const ledGL = document.getElementById('led-gl');
        if (ledCN) ledCN.className = 'nck-led' + (state.nodes.cn.state === 'ok' ? ' led-ok-cn' : (state.nodes.cn.state === 'checking' ? ' led-checking' : ''));
        if (ledGL) ledGL.className = 'nck-led' + (state.nodes.gl.state === 'ok' ? ' led-ok-gl' : (state.nodes.gl.state === 'checking' ? ' led-checking' : ''));

        // 面板文本
        const valCN = document.getElementById('ip-cn-val');
        const valGL = document.getElementById('ip-gl-val');
        if (valCN) valCN.innerText = state.nodes.cn.ip;
        if (valGL) valGL.innerText = state.nodes.gl.ip;

        // 2. 【新增/修复】刷新面板内部的延迟数值 (Label)
        const labelCN = document.getElementById('ip-cn-ms-label');
        const labelGL = document.getElementById('ip-gl-ms-label');
        if (labelCN) labelCN.innerText = msCN; // 更新面板 CN 行的延迟
        if (labelGL) labelGL.innerText = msGL; // 更新面板 GLB 行的延迟

        // 按钮
        const L = state.langLib[state.conf.lang];
        const btnConfigs = [
            { id: 't-matrix', key: 'matrix', text: L.mtx },
            { id: 't-bayOn', key: 'bayOn', text: L.bay },
            { id: 't-longShow', key: 'longShow', text: L.keep },
            { id: 't-locked', key: 'locked', text: L.lock },
            { id: 't-bindCtrl', key: 'bindCtrl', text: L.ctrl },
            { id: 't-pingShow', key: 'pingShow', text: L.ping },
            { id: 't-lang', key: null, text: L.lang },
            { id: 't-hide', key: null, text: L.hide }
        ];

        btnConfigs.forEach(cfg => {
            const el = document.getElementById(cfg.id);
            if (el) {
                el.innerText = cfg.text;
                if (cfg.key) el.classList.toggle('active', state.conf[cfg.key]);
            }
        });

        const matrixArea = document.getElementById('matrix-area');
        if (matrixArea) matrixArea.classList.toggle('hidden', !state.conf.matrix);

        renderMatrix();
        updatePanelPos();
    }

    function centerBox() {
        const box = document.getElementById('nck-main');
        if (box) {
            const x = (window.innerWidth - box.offsetWidth) / 2;
            const y = (window.innerHeight - box.offsetHeight) / 2;
            box.style.left = x + 'px';
            box.style.top = y + 'px';
            GM_setValue('nck_v378_pos', { x, y });
            updatePanelPos();
        }
    }

    function renderMatrix() {
        const grid = document.getElementById('grid-1');
        if (grid) grid.innerHTML = state.targets.map(t => `<div class="ms-node ${t.ms ? 'active' : ''}"><b>${t.id}</b>${t.ms || '--'}</div>`).join('');
    }

    function updatePanelPos() {
        const box = document.getElementById('nck-main');
        const container = document.getElementById('nck-panel-container');
        if (box && container) {
            const rect = box.getBoundingClientRect();
            container.className = rect.top > window.innerHeight / 2 ? 'dir-up' : 'dir-down';
        }
    }

    function checkNet() {
        const start = Date.now();
        state.nodes.cn.state = 'checking'; state.nodes.gl.state = 'checking';
        syncUI();

        GM_xmlhttpRequest({
            method: "GET",
            url: "https://myip.ipip.net",
            timeout: 2500,
            onload: (r) => {
                // IPIP 返回的是纯文本,形如 "IP: 1.2.3.4  来自于: 中国..."
                // 我们用正则直接提取出 IP 地址部分
                const match = r.responseText.match(/((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}/);

                if (match) {
                    state.nodes.cn.ip = match[0];
                    state.nodes.cn.ms = (Date.now() - start) + 'ms';
                    state.nodes.cn.state = 'ok';
                } else {
                    state.nodes.cn.state = 'err';
                }
                syncUI();
            },
            onerror: () => {
                state.nodes.cn.state = 'err';
                syncUI();
            }
        });

        GM_xmlhttpRequest({
            method: "GET", url: "https://www.cloudflare.com/cdn-cgi/trace", timeout: 2500,
            onload: (r) => {
                const ip = r.responseText.match(/ip=(.+)/);
                const loc = r.responseText.match(/loc=(.+)/);
                if (ip) state.nodes.gl.ip = ip[1];
                if (loc) state.nodes.code = loc[1];
                state.nodes.gl.ms = (Date.now() - start) + 'ms';
                state.nodes.gl.state = 'ok';
                syncUI();
            },
            onerror: () => { state.nodes.gl.state = 'err'; syncUI(); }
        });

        state.targets.forEach(t => {
            const s = Date.now();
            GM_xmlhttpRequest({
                method: "GET", url: t.url + "?t=" + s, timeout: 2000,
                onload: () => { t.ms = (Date.now() - s) + 'ms'; renderMatrix(); }
            });
        });
    }

    async function handleSend() {
        if (state.isProcessing) return;
        state.isProcessing = true;
        const main = document.getElementById('nck-main');

        state.nodes.cn.state = 'checking'; state.nodes.gl.state = 'checking'; syncUI();

        const isOk = await new Promise(r => {
            GM_xmlhttpRequest({
                method: "GET", url: "https://www.google.com/generate_204", timeout: 1500,
                onload: () => r(true), onerror: () => r(false), ontimeout: () => r(false)
            });
        });

        if (isOk) {
            main.classList.add('state-success');
            const selectors = ['button[aria-label*="发送"]', 'button[aria-label*="Send"]', '[data-testid="send-button"]', 'button.send-icon', 'button[type="submit"]', '#send-btn'];
            for (let s of selectors) {
                const btn = document.querySelector(s);
                if (btn && !btn.disabled) { btn.click(); break; }
            }
            setTimeout(() => main.classList.remove('state-success'), 500);
        } else {
            main.classList.add('state-error');
            setTimeout(() => main.classList.remove('state-error'), 800);
        }

        state.isProcessing = false;
        checkNet();
    }

    function inject() {
        if (document.getElementById('nck-main')) return;
        GM_addStyle(STYLES);
        const box = document.createElement('div');
        box.id = 'nck-main';
        const pos = GM_getValue('nck_v378_pos', { x: window.innerWidth - 240, y: 50 });
        box.style.left = pos.x + 'px'; box.style.top = pos.y + 'px';

        box.innerHTML = `
            <div id="nck-panel-container">
                <div id="nck-panel">
                    <div class="panel-header"><span class="panel-ver">IRONLOCK v37.5.31.beta</span><div id="ip-skk" class="ip-link-btn">IP</div></div>
                    <div class="ip-row">
                        <div class="ip-row-info"><span class="ip-label">CN <b>IPIP</b></span><span id="ip-cn-ms-label" class="ip-ms ms-cn">--</span></div>
                        <span id="ip-cn-val" class="ip-val">...</span>
                    </div>
                    <div class="ip-row">
                        <div class="ip-row-info"><span class="ip-label">GLB <b>CF</b></span><span id="ip-gl-ms-label" class="ip-ms ms-gl">--</span></div>
                        <span id="ip-gl-val" class="ip-val">...</span>
                    </div>
                    <div id="matrix-area"><div class="ms-grid" id="grid-1"></div></div>
                    <div class="btn-group">
                        <div class="t-btn" id="t-matrix"></div><div class="t-btn" id="t-bayOn"></div>
                        <div class="t-btn" id="t-longShow"></div><div class="t-btn" id="t-locked"></div>
                        <div class="t-btn" id="t-bindCtrl"></div><div class="t-btn" id="t-pingShow"></div>
                        <div class="t-btn" id="t-lang"></div><div class="t-btn" id="t-hide"></div>
                    </div>
                </div>
            </div>
            <div id="nck-trigger"><div id="led-cn" class="nck-led"></div><div id="nck-info-box"></div><div id="led-gl" class="nck-led"></div></div>
            <div id="nck-bay"><div class="bay-play"></div></div>`;

        document.body.appendChild(box);

        const configKeys = ['matrix', 'bayOn', 'longShow', 'locked', 'bindCtrl', 'pingShow'];
        configKeys.forEach(key => {
            document.getElementById('t-' + key).onclick = (e) => {
                e.preventDefault(); e.stopPropagation();
                state.conf[key] = !state.conf[key];
                GM_setValue('nck_v378_' + key, state.conf[key]);
                syncUI();
            };
        });

        document.getElementById('t-lang').onclick = (e) => {
            e.preventDefault(); e.stopPropagation();
            state.conf.lang = state.conf.lang === 'zh' ? 'en' : 'zh';
            GM_setValue('nck_v378_lang', state.conf.lang);
            syncUI();
        };

        // 最终隐藏修复
        document.getElementById('t-hide').onclick = function(e) {
            e.preventDefault();
            e.stopPropagation();
            console.log('[NCK] 执行隐藏指令');
            state.conf.visible = false;
            GM_setValue('nck_v378_vis', false);
            syncUI();
        };

        document.getElementById('ip-skk').onclick = () => GM_openInTab("https://ip.skk.moe/multi", { active: true });
        document.getElementById('nck-trigger').onclick = (e) => { e.stopPropagation(); checkNet(); };
        document.getElementById('nck-bay').onclick = (e) => { e.stopPropagation(); handleSend(); };

        box.onmousedown = (e) => {
            if (e.target.closest('#nck-panel') || state.conf.locked || e.target.closest('#nck-bay')) return;
            let ox = e.clientX - box.offsetLeft, oy = e.clientY - box.offsetTop;
            const move = (ev) => {
                box.style.left = ev.clientX - ox + 'px';
                box.style.top = ev.clientY - oy + 'px';
                updatePanelPos();
            };
            const stop = () => {
                document.removeEventListener('mousemove', move);
                GM_setValue('nck_v378_pos', { x: box.offsetLeft, y: box.offsetTop });
            };
            document.addEventListener('mousemove', move);
            document.addEventListener('mouseup', stop);
        };

        syncUI();
        checkNet();
    }

    window.addEventListener('keydown', (e) => {
        if (e.ctrlKey && e.key === 'Enter' && state.conf.bindCtrl) {
            const activeEl = document.activeElement;
            if (activeEl && (activeEl.tagName === 'TEXTAREA' || activeEl.contentEditable === 'true' || activeEl.tagName === 'INPUT')) {
                e.preventDefault();
                e.stopImmediatePropagation();
                handleSend();
            }
        }
    }, true);

    GM_registerMenuCommand("[NCK] 唤醒 / 显示界面", () => {
        state.conf.visible = true;
        GM_setValue('nck_v378_vis', true);
        syncUI();
    });

    GM_registerMenuCommand("[NCK] 重置位置居中", () => {
        state.conf.visible = true;
        GM_setValue('nck_v378_vis', true);
        centerBox();
        syncUI();
    });

    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', inject); else inject();

})();