Greasy Fork

Greasy Fork is available in English.

网页元素屏蔽器

屏蔽任意网站上的元素,支持缩略图记录和正则/简单模式屏蔽,新增动态屏蔽

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         网页元素屏蔽器
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  屏蔽任意网站上的元素,支持缩略图记录和正则/简单模式屏蔽,新增动态屏蔽
// @author       JerryChiang
// @match        *://*/*
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 添加样式
    const style = document.createElement('style');
    style.textContent = `
        .highlight {
            outline: 2px solid red !important;
            background-color: rgba(255, 0, 0, 0.1) !important;
        }
        .blocker-popup {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            padding: 20px;
            border: 1px solid #ccc;
            z-index: 9999;
            box-shadow: 0 0 10px rgba(0,0,0,0.3);
            border-radius: 8px;
            font-family: Arial, sans-serif;
            width: 600px;
            max-height: 80vh;
            overflow-y: auto;
        }
        .blocker-popup p {
            margin: 0 0 10px;
            font-size: 16px;
            color: #333;
        }
        .blocker-popup button {
            margin: 5px;
            padding: 8px 16px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.2s;
        }
        .blocker-popup button:hover {
            opacity: 0.9;
        }
        #static-block {
            background-color: #007bff;
            color: white;
        }
        #dynamic-block {
            background-color: #28a745;
            color: white;
        }
        #preview {
            background-color: #17a2b8;
            color: white;
        }
        #cancel {
            background-color: #dc3545;
            color: white;
        }
        .blocker-popup input, .blocker-popup select {
            margin: 5px;
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 14px;
        }
        .rule-row {
            display: flex;
            align-items: center;
            margin: 5px 0;
            padding: 5px;
            border-bottom: 1px solid #eee;
        }
        .blocker-list {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            padding: 20px;
            border: 1px solid #ccc;
            z-index: 9999;
            max-height: 80vh;
            overflow-y: auto;
            width: 500px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.3);
        }
        .blocker-list h3 {
            margin: 0 0 15px;
            font-size: 18px;
            color: #333;
        }
        .blocker-list ul {
            list-style: none;
            padding: 0;
        }
        .blocker-list li {
            margin: 10px 0;
            display: flex;
            align-items: center;
            width: 100%;
            padding: 5px;
            border-bottom: 1px solid #eee;
        }
        .blocker-list img {
            max-width: 400px;
            max-height: 100px;
            object-fit: contain;
            border: 1px solid #ddd;
            flex-shrink: 0;
        }
        .blocker-list button {
            margin-left: auto;
            flex-shrink: 0;
            padding: 5px 10px;
            background-color: #dc3545;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .blocker-list button:hover {
            background-color: #c82333;
        }
    `;
    document.head.appendChild(style);

    // 注册菜单
    GM_registerMenuCommand('手动屏蔽', startBlockingMode);
    GM_registerMenuCommand('按规则屏蔽', showRegexBlockInput);
    GM_registerMenuCommand('查看屏蔽记录', showBlockList);
    GM_registerMenuCommand('清除当前域名屏蔽规则(点击后需要快速点击回车)', clearDomainBlocks);

    // 进入元素选择模式
    function startBlockingMode() {
        document.body.addEventListener('mouseover', highlightElement);
        document.body.addEventListener('click', selectElement, true);
    }

    // 高亮悬停元素
    function highlightElement(event) {
        if (window.lastHighlighted) {
            window.lastHighlighted.classList.remove('highlight');
        }
        event.target.classList.add('highlight');
        window.lastHighlighted = event.target;
    }

    // 选择元素并弹出确认窗口
    function selectElement(event) {
        event.preventDefault();
        event.stopPropagation();

        document.body.removeEventListener('mouseover', highlightElement);
        document.body.removeEventListener('click', selectElement, true);

        const selectedElement = event.target;
        window.lastHighlighted.classList.remove('highlight');
        showConfirmation(selectedElement);
    }

    // 显示确认弹窗
    function showConfirmation(element) {
        const popup = document.createElement('div');
        popup.className = 'blocker-popup';
        popup.innerHTML = `
            <p>选择屏蔽方式:</p>
            <button id="static-block">静态屏蔽</button>
            <button id="dynamic-block">动态屏蔽</button>
            <button id="preview">预览</button>
            <button id="cancel">取消</button>
        `;
        document.body.appendChild(popup);

        let isPreviewHidden = false;

        const staticBtn = document.getElementById('static-block');
        staticBtn.addEventListener('click', async () => {
            staticBtn.disabled = true;
            try {
                await saveBlockWithThumbnail(element, 'static');
                element.style.display = 'none';
                document.body.removeChild(popup);
            } catch (e) {
                console.error('静态屏蔽失败:', e);
                staticBtn.disabled = false;
            }
        }, { once: true });

        const dynamicBtn = document.getElementById('dynamic-block');
        dynamicBtn.addEventListener('click', async () => {
            dynamicBtn.disabled = true;
            try {
                await saveBlockWithThumbnail(element, 'dynamic');
                applyDynamicBlocks();
                document.body.removeChild(popup);
            } catch (e) {
                console.error('动态屏蔽失败:', e);
                dynamicBtn.disabled = false;
            }
        }, { once: true });

        document.getElementById('preview').addEventListener('click', () => {
            if (!isPreviewHidden) {
                element.style.display = 'none';
                isPreviewHidden = true;
            } else {
                element.style.display = '';
                isPreviewHidden = false;
            }
        });

        document.getElementById('cancel').addEventListener('click', () => {
            document.body.removeChild(popup);
        });
    }

    // 保存屏蔽信息并生成缩略图
    async function saveBlockWithThumbnail(element, type = 'static') {
        const domain = window.location.hostname;
        const selector = getSelector(element);

        const rect = element.getBoundingClientRect();
        const width = rect.width;
        const height = rect.height;
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        canvas.width = width;
        canvas.height = height;

        let thumbnail = null;
        try {
            const computedStyle = window.getComputedStyle(element);
            ctx.fillStyle = computedStyle.backgroundColor || '#ffffff';
            ctx.fillRect(0, 0, width, height);
            if (element.textContent.trim()) {
                ctx.fillStyle = computedStyle.color || '#000000';
                ctx.font = `${computedStyle.fontSize || '16px'} ${computedStyle.fontFamily || 'Arial'}`;
                ctx.textAlign = 'left';
                ctx.textBaseline = 'top';
                const paddingLeft = parseInt(computedStyle.paddingLeft) || 0;
                const paddingTop = parseInt(computedStyle.paddingTop) || 0;
                wrapText(ctx, element.textContent.trim(), paddingLeft, paddingTop, width - paddingLeft * 2, parseInt(computedStyle.fontSize) || 16);
            }
            let scale = Math.min(400 / width, 100 / height, 1);
            const thumbnailCanvas = document.createElement('canvas');
            thumbnailCanvas.width = width * scale;
            thumbnailCanvas.height = height * scale;
            const thumbnailCtx = thumbnailCanvas.getContext('2d');
            thumbnailCtx.drawImage(canvas, 0, 0, thumbnailCanvas.width, thumbnailCanvas.height);
            thumbnail = thumbnailCanvas.toDataURL('image/png');
        } catch (e) {
            console.error('生成缩略图失败:', e);
        }

        if (type === 'static') {
            let blocks = GM_getValue('blocks', {});
            if (!blocks[domain]) blocks[domain] = [];
            if (!blocks[domain].some(item => item.selector === selector)) {
                blocks[domain].push({ selector, thumbnail: thumbnail || null, type: 'static' });
                GM_setValue('blocks', blocks);
            }
        } else if (type === 'dynamic') {
            const className = element.className.split(' ').filter(c => c)[0] || '';
            let dynamicBlocks = GM_getValue('dynamicBlocks', {});
            if (!dynamicBlocks[domain]) dynamicBlocks[domain] = [];
            if (!dynamicBlocks[domain].some(item => item.className === className)) {
                dynamicBlocks[domain].push({ className, thumbnail: thumbnail || null, type: 'dynamic' });
                GM_setValue('dynamicBlocks', dynamicBlocks);
            }
        }
        return true;
    }

    // 辅助函数:将文本换行绘制到 canvas
    function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
        const words = text.split(' ');
        let line = '';
        let currentY = y;
        for (let i = 0; i < words.length; i++) {
            const testLine = line + words[i] + ' ';
            const metrics = ctx.measureText(testLine);
            const testWidth = metrics.width;
            if (testWidth > maxWidth && i > 0) {
                ctx.fillText(line, x, currentY);
                line = words[i] + ' ';
                currentY += lineHeight;
            } else {
                line = testLine;
            }
        }
        ctx.fillText(line, x, currentY);
    }

    // 生成简单 CSS 选择器
    function getSelector(element) {
        if (element.id) return `#${element.id}`;
        let path = [];
        while (element && element.nodeType === Node.ELEMENT_NODE) {
            let selector = element.tagName.toLowerCase();
            if (element.className && typeof element.className === 'string') {
                selector += '.' + element.className.trim().replace(/\s+/g, '.');
            }
            path.unshift(selector);
            element = element.parentElement;
        }
        return path.join(' > ');
    }

    // 应用屏蔽规则(静态 + 动态 + 正则)
    function applyBlocks() {
        const domain = window.location.hostname;
        const blocks = GM_getValue('blocks', {});
        const dynamicBlocks = GM_getValue('dynamicBlocks', {});
        const regexBlocks = GM_getValue('regexBlocks', {});

        // 应用静态屏蔽
        if (blocks[domain]) {
            blocks[domain].forEach(item => {
                try {
                    document.querySelectorAll(item.selector).forEach(el => {
                        if (!el.closest('.blocker-popup') && !el.closest('.blocker-list')) {
                            el.style.display = 'none';
                        }
                    });
                } catch (e) {
                    console.error(`无法应用选择器: ${item.selector}`, e);
                }
            });
        }

        // 应用动态屏蔽
        if (dynamicBlocks[domain]) {
            dynamicBlocks[domain].forEach(rule => {
                const elements = document.getElementsByClassName(rule.className);
                Array.from(elements).forEach(el => {
                    if (!el.closest('.blocker-popup') && !el.closest('.blocker-list')) {
                        el.style.display = 'none';
                    }
                });
            });
        }

        // 应用正则屏蔽
        if (regexBlocks[domain]) {
            regexBlocks[domain].forEach(rule => {
                try {
                    const regex = new RegExp(rule.regex);
                    const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
                        acceptNode: (node) => {
                            const parent = node.parentElement;
                            return (parent && (parent.closest('.blocker-popup') || parent.closest('.blocker-list')))
                                ? NodeFilter.FILTER_REJECT
                                : NodeFilter.FILTER_ACCEPT;
                        }
                    }, false);
                    let node;
                    while (node = walker.nextNode()) {
                        if (regex.test(node.textContent)) {
                            let element = node.parentElement;
                            for (let i = 0; i < rule.level; i++) {
                                if (element.parentElement) element = element.parentElement;
                                else break;
                            }
                            element.style.display = 'none';
                        }
                    }
                } catch (e) {
                    console.error(`无法应用规则: ${rule.regex}`, e);
                }
            });
        }
    }

    // 应用动态屏蔽(独立函数已无必要,保留为空兼容旧调用)
    function applyDynamicBlocks() {
        applyBlocks(); // 直接调用统一应用函数
    }

    // 显示屏蔽记录窗口
    function showBlockList() {
        const domain = window.location.hostname;
        const blocks = GM_getValue('blocks', {});
        const dynamicBlocks = GM_getValue('dynamicBlocks', {});
        const blockList = (blocks[domain] || []).concat(dynamicBlocks[domain] || []);

        const listWindow = document.createElement('div');
        listWindow.className = 'blocker-list';
        listWindow.innerHTML = `
            <h3>当前域名屏蔽记录 (${domain})</h3>
            <ul id="block-items"></ul>
            <button id="close-list">关闭</button>
        `;
        document.body.appendChild(listWindow);

        const ul = document.getElementById('block-items');
        if (blockList.length === 0) {
            ul.innerHTML = '<li>暂无屏蔽记录</li>';
        } else {
            blockList.forEach((item, index) => {
                const li = document.createElement('li');
                if (item.thumbnail) {
                    const img = document.createElement('img');
                    img.src = item.thumbnail;
                    li.appendChild(img);
                } else {
                    li.textContent = item.type === 'static' ? item.selector : item.className;
                }
                const unblockBtn = document.createElement('button');
                unblockBtn.textContent = '取消屏蔽';
                unblockBtn.addEventListener('click', () => {
                    removeBlock(domain, index, item.type);
                    listWindow.remove();
                    applyBlocks();
                    showBlockList();
                });
                li.appendChild(unblockBtn);
                ul.appendChild(li);
            });
        }

        document.getElementById('close-list').addEventListener('click', () => {
            document.body.removeChild(listWindow);
        });
    }

    // 删除屏蔽记录
    function removeBlock(domain, index, type) {
        if (type === 'static') {
            let blocks = GM_getValue('blocks', {});
            if (blocks[domain] && blocks[domain][index]) {
                blocks[domain].splice(index, 1);
                if (blocks[domain].length === 0) delete blocks[domain];
                GM_setValue('blocks', blocks);
            }
        } else if (type === 'dynamic') {
            let dynamicBlocks = GM_getValue('dynamicBlocks', {});
            const staticCount = (GM_getValue('blocks', {})[domain] || []).length;
            const dynamicIndex = index - staticCount;
            if (dynamicBlocks[domain] && dynamicBlocks[domain][dynamicIndex]) {
                dynamicBlocks[domain].splice(dynamicIndex, 1);
                if (dynamicBlocks[domain].length === 0) delete dynamicBlocks[domain];
                GM_setValue('dynamicBlocks', dynamicBlocks);
            }
        }
    }

    // 显示规则屏蔽输入和管理窗口
    function showRegexBlockInput() {
        const domain = window.location.hostname;
        let regexBlocks = GM_getValue('regexBlocks', {});
        let rules = regexBlocks[domain] || [];

        const popup = document.createElement('div');
        popup.className = 'blocker-popup';
        popup.innerHTML = `
            <p>设置屏蔽规则(层级:0 表示当前元素,1 表示父元素,依此类推,请先点击预览,避免失误将整个页面清除):</p>
            <button id="regex-mode">正则模式</button>
            <button id="simple-mode">简单模式</button>
            <div id="input-container"></div>
            <div id="rules-list">
                <h3>当前规则列表</h3>
                <div id="rules-rows"></div>
            </div>
            <div>
                <button id="add-rule-row">新增规则</button>
                <button id="save-rules">保存</button>
                <button id="cancel-rule">取消</button>
            </div>
        `;
        document.body.appendChild(popup);

        document.getElementById('regex-mode').style.backgroundColor = '#007bff';
        document.getElementById('regex-mode').style.color = 'white';
        document.getElementById('simple-mode').style.backgroundColor = '#007bff';
        document.getElementById('simple-mode').style.color = 'white';
        document.getElementById('add-rule-row').style.backgroundColor = '#17a2b8';
        document.getElementById('add-rule-row').style.color = 'white';
        document.getElementById('save-rules').style.backgroundColor = '#17a2b8';
        document.getElementById('save-rules').style.color = 'white';
        document.getElementById('cancel-rule').style.backgroundColor = '#dc3545';
        document.getElementById('cancel-rule').style.color = 'white';

        let isDragging = false;
        let currentX;
        let currentY;
        let initialX;
        let initialY;

        popup.addEventListener('mousedown', (e) => {
            if (e.target.tagName !== 'BUTTON' && e.target.tagName !== 'INPUT' && e.target.tagName !== 'SELECT') {
                isDragging = true;
                initialX = e.clientX - currentX;
                initialY = e.clientY - currentY;
                popup.style.cursor = 'grabbing';
            }
        });

        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                e.preventDefault();
                currentX = e.clientX - initialX;
                currentY = e.clientY - initialY;
                popup.style.left = `${currentX + (popup.offsetWidth / 2)}px`;
                popup.style.top = `${currentY + (popup.offsetHeight / 2)}px`;
                popup.style.transform = 'none';
            }
        });

        document.addEventListener('mouseup', () => {
            isDragging = false;
            popup.style.cursor = 'default';
        });

        currentX = window.innerWidth / 2 - popup.offsetWidth / 2;
        currentY = window.innerHeight / 2 - popup.offsetHeight / 2;
        popup.style.left = `${currentX + (popup.offsetWidth / 2)}px`;
        popup.style.top = `${currentY + (popup.offsetHeight / 2)}px`;

        const inputContainer = document.getElementById('input-container');
        const rulesRows = document.getElementById('rules-rows');
        let isSimpleMode = false;
        let tempRules = [...rules];

        function showRegexInput() {
            inputContainer.innerHTML = `
                <div class="rule-row">
                    <input type="text" class="regex-input" placeholder="正则规则" />
                    <input type="number" class="level-input" placeholder="层级" value="0" min="0" />
                    <button class="preview-rule">预览</button>
                </div>
            `;
            document.querySelector('.preview-rule').style.backgroundColor = '#28a745';
            document.querySelector('.preview-rule').style.color = 'white';
            attachPreviewListeners();
        }

        function showSimpleInput() {
            inputContainer.innerHTML = `
                <div class="rule-row">
                    <select class="logic-select">
                        <option value="contains">包含</option>
                        <option value="not-contains">不包含</option>
                        <option value="equals">等于</option>
                    </select>
                    <input type="text" class="simple-input" placeholder="文本内容" />
                    <input type="number" class="level-input" placeholder="层级" value="0" min="0" />
                    <button class="preview-rule">预览</button>
                </div>
            `;
            document.querySelector('.preview-rule').style.backgroundColor = '#28a745';
            document.querySelector('.preview-rule').style.color = 'white';
            attachPreviewListeners();
        }

        document.getElementById('regex-mode').addEventListener('click', () => {
            isSimpleMode = false;
            showRegexInput();
        });
        document.getElementById('simple-mode').addEventListener('click', () => {
            isSimpleMode = true;
            showSimpleInput();
        });

        showRegexInput();

        function renderRules() {
            rulesRows.innerHTML = '';
            if (tempRules.length === 0) {
                rulesRows.innerHTML = '<p>暂无规则</p>';
                return;
            }
            tempRules.forEach((rule, index) => {
                const row = document.createElement('div');
                row.className = 'rule-row';
                row.innerHTML = `
                    <input type="text" class="rule-regex" value="${rule.regex}" />
                    <input type="number" class="rule-level" value="${rule.level}" min="0" />
                    <button class="delete-rule">删除</button>
                `;
                const regexInput = row.querySelector('.rule-regex');
                const levelInput = row.querySelector('.rule-level');
                const deleteBtn = row.querySelector('.delete-rule');

                regexInput.addEventListener('input', () => {
                    tempRules[index].regex = regexInput.value;
                });
                levelInput.addEventListener('input', () => {
                    tempRules[index].level = parseInt(levelInput.value, 10);
                });

                deleteBtn.addEventListener('click', () => {
                    tempRules.splice(index, 1);
                    renderRules();
                });

                rulesRows.appendChild(row);
            });
        }

        document.getElementById('add-rule-row').addEventListener('click', () => {
            let regex, level;
            if (isSimpleMode) {
                const logic = inputContainer.querySelector('.logic-select').value;
                const text = inputContainer.querySelector('.simple-input').value.trim();
                level = parseInt(inputContainer.querySelector('.level-input').value, 10);
                if (!text || isNaN(level) || level < 0) {
                    alert('请输入有效的文本和层级');
                    return;
                }
                regex = convertSimpleToRegex(logic, text);
            } else {
                regex = inputContainer.querySelector('.regex-input').value.trim();
                level = parseInt(inputContainer.querySelector('.level-input').value, 10);
                if (!regex || isNaN(level) || level < 0) {
                    alert('请输入有效的正则规则和层级');
                    return;
                }
            }
            tempRules.push({ regex, level });
            renderRules();
            if (isSimpleMode) {
                inputContainer.querySelector('.simple-input').value = '';
                inputContainer.querySelector('.level-input').value = '0';
            } else {
                inputContainer.querySelector('.regex-input').value = '';
                inputContainer.querySelector('.level-input').value = '0';
            }
        });

        document.getElementById('save-rules').addEventListener('click', () => {
            regexBlocks[domain] = tempRules;
            GM_setValue('regexBlocks', regexBlocks);
            applyBlocks();
            document.body.removeChild(popup);
        });

        document.getElementById('cancel-rule').addEventListener('click', () => {
            document.body.removeChild(popup);
        });

        function convertSimpleToRegex(logic, text) {
            const escapedText = text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
            switch (logic) {
                case 'contains': return `.*${escapedText}.*`;
                case 'not-contains': return `^(?!.*${escapedText}).*$`;
                case 'equals': return `^${escapedText}$`;
                default: return escapedText;
            }
        }

        function attachPreviewListeners() {
            const previewBtn = inputContainer.querySelector('.preview-rule');
            let previewActive = false;
            let affectedElements = [];

            previewBtn.addEventListener('click', () => {
                if (!previewActive) {
                    let regex, level;
                    if (isSimpleMode) {
                        const logic = inputContainer.querySelector('.logic-select').value;
                        const text = inputContainer.querySelector('.simple-input').value.trim();
                        level = parseInt(inputContainer.querySelector('.level-input').value, 10);
                        if (!text || isNaN(level) || level < 0) {
                            alert('请输入有效的文本和层级');
                            return;
                        }
                        regex = convertSimpleToRegex(logic, text);
                    } else {
                        regex = inputContainer.querySelector('.regex-input').value.trim();
                        level = parseInt(inputContainer.querySelector('.level-input').value, 10);
                        if (!regex || isNaN(level) || level < 0) {
                            alert('请输入有效的正则规则和层级');
                            return;
                        }
                    }
                    try {
                        const ruleRegex = new RegExp(regex);
                        const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
                            acceptNode: (node) => {
                                const parent = node.parentElement;
                                return (parent && (parent.closest('.blocker-popup') || parent.closest('.blocker-list')))
                                    ? NodeFilter.FILTER_REJECT
                                    : NodeFilter.FILTER_ACCEPT;
                            }
                        }, false);
                        let node;
                        affectedElements = [];
                        while (node = walker.nextNode()) {
                            if (ruleRegex.test(node.textContent)) {
                                let element = node.parentElement;
                                for (let i = 0; i < level; i++) {
                                    if (element.parentElement) element = element.parentElement;
                                    else break;
                                }
                                affectedElements.push(element);
                                element.style.display = 'none';
                            }
                        }
                        previewActive = true;
                        previewBtn.textContent = '取消预览';
                    } catch (e) {
                        alert('正则表达式无效,请检查输入');
                    }
                } else {
                    affectedElements.forEach(el => el.style.display = '');
                    affectedElements = [];
                    previewActive = false;
                    previewBtn.textContent = '预览';
                }
            });
        }

        renderRules();
    }

    // 清除当前域名屏蔽规则
    function clearDomainBlocks() {
        const domain = window.location.hostname;
        if (window.confirm(`是否确认清除当前域名 (${domain}) 下的所有屏蔽规则?`)) {
            let blocks = GM_getValue('blocks', {});
            let dynamicBlocks = GM_getValue('dynamicBlocks', {});
            let regexBlocks = GM_getValue('regexBlocks', {});

            delete blocks[domain];
            delete dynamicBlocks[domain];
            delete regexBlocks[domain];

            GM_setValue('blocks', blocks);
            GM_setValue('dynamicBlocks', dynamicBlocks);
            GM_setValue('regexBlocks', regexBlocks);

            // 刷新页面以恢复显示
            window.location.reload();
        }
    }

    // 页面加载时立即应用所有屏蔽规则
    applyBlocks();
    const observer = new MutationObserver(() => applyBlocks());
    observer.observe(document.body, { childList: true, subtree: true });
})();