必应/Edge 美化 & 效能工具 (Robust & Final Fix)

确保在页面动态重绘后脚本依然生效。修复了网址屏蔽功能中(1)移除规则后部分结果仍被隐藏的问题;(2)因Bing链接格式不标准导致无法创建屏蔽规则的问题。

// ==UserScript==
// @name         必应/Edge 美化 & 效能工具 (Robust & Final Fix)
// @namespace    Violentmonkey Scripts
// @version      17.8
// @description  确保在页面动态重绘后脚本依然生效。修复了网址屏蔽功能中(1)移除规则后部分结果仍被隐藏的问题;(2)因Bing链接格式不标准导致无法创建屏蔽规则的问题。
// @author       WJH
// @match        https://*.bing.com/*
// @match        https://*.msn.cn/*
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // --- 1. CSS样式注入 (粒子特效已增强) ---
    const styles = `
        /* --- A. 通用UI样式 --- */
        #userscript-ui-container { position: fixed; bottom: 25px; right: 25px; z-index: 10001; display: flex; flex-direction: column; align-items: flex-end; gap: 15px; }
        .userscript-btn { width: 44px; height: 44px; background-color: rgba(255, 255, 255, 0.9); border-radius: 50%; display: flex; justify-content: center; align-items: center; cursor: pointer; box-shadow: 0 4px 12px rgba(0,0,0,0.15); transition: all 0.2s ease; }
        .userscript-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 16px rgba(0,0,0,0.2); }
        .userscript-btn svg { width: 24px; height: 24px; color: #333; }
        #go-to-top-btn { opacity: 0; pointer-events: none; transform: translateY(10px); }
        #go-to-top-btn.visible { opacity: 1; pointer-events: auto; transform: translateY(0); }
        #manage-blocklist-btn { transform: scale(0.9); opacity: 0.7; }
        #manage-blocklist-btn:hover { opacity: 1; transform: scale(1); }

        /* --- B. 必应搜索 (bing.com) 美化样式 --- */
        body[data-host="bing"] {
            background-image: url('https://raw.githubusercontent.com/WJH-makers/markdown_photos/main/images/sea.png') !important;
            background-size: cover !important; background-position: center center !important;
            background-attachment: fixed !important; background-repeat: no-repeat !important;
        }

        /* B.1 搜索结果样式 */
        #b_content { padding-top: 20px !important; }
        #b_results { background: none !important; box-shadow: none !important; border: none !important; }
        .sb_count { font-size: 13px !important; color: #f0f0f0 !important; text-shadow: 1px 1px 2px rgba(0,0,0,0.6); }
        body[data-host="bing"] #b_pag { opacity: 0; pointer-events: none; height: 10px; }
        body[data-host="bing"] #infinite-scroll-loader { text-align: center; padding: 20px; color: #FFF; text-shadow: 1px 1px 2px #000; font-weight: bold; min-height: 24px; }

        body[data-host="bing"] #b_results > li.b_algo,
        body[data-host="bing"] #b_results > li.b_ans:not(:has(.lite-entcard-main)) {
            background-color: rgba(255, 255, 255, 0.85) !important; backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
            border: 1px solid rgba(224, 224, 224, 0.7); border-radius: 12px !important;
            padding: 20px 24px 20px 50px !important;
            margin-bottom: 16px !important;
            max-width: 1500px !important;
            min-width: 1000px;
            box-shadow: 0 1px 4px rgba(0,0,0,0.05); transition: all 0.2s ease-in-out;
            position: relative;
            display: block !important; box-sizing: border-box;
        }
        body[data-host="bing"] #b_results > li.b_algo:hover,
        body[data-host="bing"] #b_results > li.b_ans:not(:has(.lite-entcard-main)):hover {
            box-shadow: 0 5px 15px rgba(0,0,0,0.1) !important; transform: translateY(-2px); border-color: rgba(208, 208, 208, 0.9);
        }

        /* B.2 隐藏广告和无关模块 */
        body[data-host="bing"] .b_header_bg, body[data-host="bing"] #est_switch,
        body[data-host="bing"] #b_pole, body[data-host="bing"] li.b_ad, body[data-host="bing"] #b_footer,
        body[data-host="bing"] li.b_msg, body[data-host="bing"] #adstop_gradiant_separator,
        body[data-host="bing"] .b_rrsr, body[data-host="bing"] #b_context,
        body[data-host="bing"] li.b_ans:has(.rqnaContainerwithfeedback),
        body[data-host="bing"] li.b_ans:has(.lite-entcard-main),
        body[data-host="bing"] li.b_ans:has(#brsv3), body[data-host="bing"] li.b_ans:has(#inline_rs),
        body[data-host="bing"] li.b_ans:has(#brs_list),
        body[data-host="bing"] div[data-tag="RichRSRailOuterWrapper"]
        { display: none !important; }

        /* B.3 效能搜索工具栏样式 */
        #power-search-toolbar { background-color: rgba(255, 255, 255, 0.88); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); padding: 10px 16px; border-radius: 10px; margin: 0px 0px 50px; max-width: 960px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); border: 1px solid rgba(224, 224, 224, 0.7); }
        .power-search-group { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
        .power-search-group:not(:last-child) { margin-bottom: 12px; }
        .power-search-group-label { font-size: 14px; font-weight: bold; color: #333; margin-right: 8px; flex-shrink: 0; }
        .power-search-btn { font-size: 13px; color: #444; text-decoration: none; padding: 5px 12px; border-radius: 16px; background-color: rgba(0,0,0,0.05); border: 1px solid rgba(0,0,0,0.08); transition: all 0.2s ease; }
        .power-search-btn:hover { background-color: rgba(0,0,0,0.1); border-color: rgba(0,0,0,0.15); }
        .power-search-btn.active { font-weight: bold; color: #fff !important; background-color: #0078d4; border-color: #0078d4; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
        .filter-divider { width: 100%; height: 1px; background-color: rgba(0,0,0,0.1); margin: 12px 0 12px 0; }

        /* B.4 网站屏蔽功能样式 */
        .block-site-btn { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); width: 28px; height: 28px; border-radius: 50%; background-color: rgba(0,0,0,0.05); border: 1px solid rgba(0,0,0,0.1); display: flex; justify-content: center; align-items: center; cursor: pointer; opacity: 0.2; transition: all 0.2s ease; }
        li.b_algo:hover .block-site-btn, li.b_ans:hover .block-site-btn { opacity: 0.6; }
        .block-site-btn:hover { opacity: 1; background-color: #f44336; border-color: #f44336; color: white; transform: translateY(-50%) scale(1.1); }
        .block-site-btn svg { width: 16px; height: 16px; }

        /* B.5 屏蔽列表管理弹窗样式 */
        #blocklist-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 20000; display: none; justify-content: center; align-items: center; }
        #blocklist-modal { background-color: #fff; padding: 24px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); width: 90%; max-width: 500px; max-height: 80vh; display: flex; flex-direction: column; }
        #blocklist-modal h2 { margin: 0 0 16px; font-size: 20px; color: #333; }
        #blocklist-modal-list { list-style: none; padding: 0; margin: 0; overflow-y: auto; flex-grow: 1; border: 1px solid #eee; border-radius: 8px; }
        #blocklist-modal-list li { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; border-bottom: 1px solid #eee; font-size: 15px; color: #444; word-break: break-all; }
        #blocklist-modal-list li:last-child { border-bottom: none; }
        .remove-block-btn { font-size: 13px; color: #0078d4; background: none; border: none; cursor: pointer; font-weight: bold; margin-left: 10px; flex-shrink: 0; }
        .remove-block-btn:hover { text-decoration: underline; }
        #blocklist-modal-close { margin-top: 20px; padding: 8px 16px; background-color: #0078d4; color: white; border: none; border-radius: 8px; cursor: pointer; align-self: flex-end; }

        /* --- C & D 部分样式无需修改 --- */
        body[data-host="msn"] { background: #F7F7F7 !important; }
        body[data-host="msn"] .consumption-page-structure { display: flex !important; justify-content: center !important; grid-template-columns: 1fr !important; }
        body[data-host="msn"] .consumption-page-gridarea_content { width: 90% !important; max-width: 1200px !important; }
        body[data-host="msn"] div[data-t*="AboveRiverBlock"], body[data-host="msn"] views-native-ad, body[data-host="msn"] .feed-section[data-t*="River"], body[data-host="msn"] .ad-slot-placeholder, body[data-host="msn"] common-header, body[data-host="msn"] .consumption-page-gridarea_action, body[data-host="msn"] views-right-rail, body[data-host="msn"] .railBelow_c2, body[data-host="msn"] .consumption-page-feed-wrapper, body[data-host="msn"] ms-toast, body[data-host="msn"] .consumption-page-eocb, body[data-host="msn"] common-footer, body[data-host="msn"] #footer { display: none !important; visibility: hidden !important; }

        /* [MODIFIED] 增强粒子效果 */
        .sparkle-particle {
            position: fixed;
            width: 12px; /* 增大尺寸 */
            height: 12px; /* 增大尺寸 */
            border-radius: 50%;
            pointer-events: none;
            z-index: 99999;
            opacity: 1;
            /* 增长动画时间 */
            transition: transform 1.5s cubic-bezier(0.17, 0.84, 0.44, 1), opacity 1.5s cubic-bezier(0.32, 0, 0.67, 0);
        }
    `;
    GM_addStyle(styles);


    // --- 2. JavaScript 逻辑部分 (已重构以提升性能和修复Bug) ---
    const BLOCKLIST_KEY = 'userscript_site_blocklist_v2_prefixes';
    const blocklistManager = {
        _blocklist: null,
        getBlocklist: function() {
            if (this._blocklist === null) {
                this._blocklist = JSON.parse(localStorage.getItem(BLOCKLIST_KEY) || '[]');
            }
            return this._blocklist;
        },
        saveBlocklist: function(list) {
            const uniqueList = Array.from(new Set(list));
            this._blocklist = uniqueList;
            localStorage.setItem(BLOCKLIST_KEY, JSON.stringify(uniqueList));
        },
        addRule: function(rule) {
            const list = this.getBlocklist();
            const cleanRule = rule.replace(/^https?:\/\//, '').replace(/\/$/, '');
            if (cleanRule && !list.includes(cleanRule)) {
                list.push(cleanRule);
                this.saveBlocklist(list);
            }
        },
        removeRule: function(rule) {
            let list = this.getBlocklist();
            list = list.filter(item => item !== rule);
            this.saveBlocklist(list);
        }
    };

    // [OPTIMIZED & FIXED v2] Centralized function to manage element visibility
    function applyBlocklistToElement(result, blocklist) {
        const unwantedSelectors = '#brsv3, #inline_rs, .rqnaContainerwithfeedback, .lite-entcard-main';
        const cite = result.querySelector('cite');

        if (!cite) {
            if (result.querySelector(unwantedSelectors)) {
                result.style.setProperty('display', 'none', 'important');
            }
            return;
        }

        try {
            // [FIX] Clean the cite text to get a valid URL, removing extra characters added by Bing.
            const rawCiteText = cite.textContent;
            const urlText = rawCiteText.split(' ')[0]; // Takes only the part before the first space.

            const normalizedUrl = (urlText.startsWith('http') ? urlText : 'http://' + urlText)
                .replace(/^https?:\/\//, '')
                .replace(/^www\./, '');

            const isBlockedByUser = blocklist.some(rule => normalizedUrl.startsWith(rule));

            if (isBlockedByUser) {
                result.style.setProperty('display', 'none', 'important');
            } else {
                const isUnwantedModule = result.querySelector(unwantedSelectors);
                if (isUnwantedModule) {
                    result.style.setProperty('display', 'none', 'important');
                } else {
                    result.style.removeProperty('display');
                }
            }
        } catch (e) {
            // Invalid URL, ignore this element
        }
    }

    function applyBlocklist() {
        const blocklist = blocklistManager.getBlocklist();
        document.querySelectorAll('li.b_algo, li.b_ans').forEach(result => applyBlocklistToElement(result, blocklist));
    }

    // [FIXED] Injects button and handles creation of block rules
    function injectBlockButtonToElement(result) {
        if (result.dataset.blockBtnInjected || !result.matches('li.b_algo, li.b_ans:not(:has(.lite-entcard-main))')) return;
        result.dataset.blockBtnInjected = 'true';
        const cite = result.querySelector('cite');
        if (!cite) return;
        const btn = document.createElement('div');
        btn.className = 'block-site-btn';
        btn.title = `屏蔽此网站或路径...`;
        btn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" /></svg>`;
        btn.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            try {
                // [FIX] Clean the cite text to get a valid URL before parsing.
                const rawCiteText = cite.textContent;
                const cleanUrlText = rawCiteText.split(' ')[0]; // Takes only the part before the first space.

                const url = new URL(cleanUrlText.startsWith('http') ? cleanUrlText : 'http://' + cleanUrlText);
                const hostname = url.hostname.replace(/^www\./, '');
                const pathSegments = url.pathname.split('/').filter(Boolean);
                let ruleToBlock = pathSegments.length > 0 ? `${hostname}/${pathSegments[0]}` : hostname;
                if (confirm(`确定要屏蔽此前缀吗?\n\n${ruleToBlock}`)) {
                    blocklistManager.addRule(ruleToBlock);
                    applyBlocklist();
                }
            } catch (error) {
                console.error("Error creating block rule:", error);
                alert('无法从此链接生成屏蔽规则,链接可能格式不正确。');
            }
        });
        result.appendChild(btn);
    }
    function injectBlockButtons() {
        document.querySelectorAll('li.b_algo, li.b_ans').forEach(injectBlockButtonToElement);
    }

    // --- Modal and UI functions (Unchanged) ---
    function createBlocklistModal() {
        if (document.getElementById('blocklist-modal-overlay')) return;
        const modalOverlay = document.createElement('div');
        modalOverlay.id = 'blocklist-modal-overlay';
        modalOverlay.innerHTML = `<div id="blocklist-modal"><h2>已屏蔽的规则</h2><ul id="blocklist-modal-list"></ul><button id="blocklist-modal-close">关闭</button></div>`;
        document.body.appendChild(modalOverlay);
        const modal = modalOverlay.querySelector('#blocklist-modal');
        const closeBtn = modalOverlay.querySelector('#blocklist-modal-close');
        modal.addEventListener('click', e => e.stopPropagation());
        modalOverlay.addEventListener('click', () => modalOverlay.style.display = 'none');
        closeBtn.addEventListener('click', () => modalOverlay.style.display = 'none');
    }
    function populateAndShowModal() {
        const modalOverlay = document.getElementById('blocklist-modal-overlay');
        const listElement = document.getElementById('blocklist-modal-list');
        if (!modalOverlay || !listElement) return;
        listElement.innerHTML = '';
        const blocklist = blocklistManager.getBlocklist();
        if (blocklist.length === 0) {
            listElement.innerHTML = '<li>列表为空</li>';
        } else {
            blocklist.sort().forEach(rule => {
                const li = document.createElement('li');
                li.textContent = rule;
                const removeBtn = document.createElement('button');
                removeBtn.className = 'remove-block-btn';
                removeBtn.textContent = '移除';
                removeBtn.onclick = () => {
                    blocklistManager.removeRule(rule);
                    applyBlocklist();
                    populateAndShowModal();
                };
                li.appendChild(removeBtn);
                listElement.appendChild(li);
            });
        }
        modalOverlay.style.display = 'flex';
    }
    function createPowerSearchToolbar() {
        let toolbar = document.getElementById('power-search-toolbar');
        if (!toolbar) {
            toolbar = document.createElement('div');
            toolbar.id = 'power-search-toolbar';
            const container = document.getElementById('b_tween');
            if (!container) return;
            container.after(toolbar);
        }
        toolbar.innerHTML = '';
        toolbar.appendChild(createTimeFilterGroup());
        toolbar.appendChild(document.createElement('div')).className = 'filter-divider';
        const searchInput = document.getElementById('sb_form_q');
        if (!searchInput) return;
        const parsed = parseQuery(searchInput.value);
        const modeGroup = createGroup('搜索模式:');
        const modes = [
            { label: '智能排序', mode: 'smart', isDefault: true },
            { label: '精确匹配', mode: 'exact' },
            { label: '标题包含', mode: 'intitle' }
        ];
        modes.forEach(modeInfo => {
            const btn = createPowerButton(modeInfo.label, generateQuery({ ...parsed, mode: modeInfo.mode }));
            if (parsed.mode === modeInfo.mode || (parsed.mode === 'smart' && modeInfo.isDefault)) {
                btn.classList.add('active');
            }
            modeGroup.appendChild(btn);
        });
        toolbar.appendChild(modeGroup);
        toolbar.appendChild(document.createElement('div')).className = 'filter-divider';
        const filetypeGroup = createGroup(null);
        ['PDF', 'PPT', 'DOC', 'XLS'].forEach(type => {
            const filetype = type.toLowerCase();
            const btn = createPowerButton(type, generateQuery({ ...parsed, filetype }));
            if (parsed.filetype === filetype) btn.classList.add('active');
            filetypeGroup.appendChild(btn);
        });
        const siteGroup = createGroup(null);
        [
            { name: '知乎', domain: 'zhihu.com' },
            { name: 'Bilibili', domain: 'bilibili.com' },
            { name: '少数派', domain: 'sspai.com' },
            { name: 'GitHub', domain: 'github.com' },
            { name: '维基百科', domain: 'wikipedia.org' },
            { name: '豆瓣', domain: 'douban.com' }
        ].forEach(site => {
            const btn = createPowerButton(site.name, generateQuery({ ...parsed, site: site.domain }));
            if (parsed.site === site.domain) btn.classList.add('active');
            siteGroup.appendChild(btn);
        });
        const filterContainer = document.createElement('div');
        filterContainer.style.display = 'flex';
        filterContainer.style.flexDirection = 'column';
        filterContainer.style.gap = '12px';
        filterContainer.appendChild(filetypeGroup);
        filterContainer.appendChild(siteGroup);
        const filterWrapper = document.createElement('div');
        filterWrapper.style.display = 'flex';
        filterWrapper.style.alignItems = 'flex-start';
        filterWrapper.appendChild(createGroup('筛选器:')).querySelector('.power-search-group-label').style.paddingTop = '5px';
        filterWrapper.appendChild(filterContainer);
        toolbar.appendChild(filterWrapper);
    }
    function createTimeFilterGroup() {
        const group = createGroup('时间范围:');
        const filters = [
            { label: '全部', param: '' },
            { label: '一天内', param: 'ez1' },
            { label: '一周内', param: 'ez2' },
            { label: '一月内', param: 'ez3' },
            { label: '一年内', param: 'ez4' }
        ];
        const currentUrl = new URL(window.location.href);
        const currentFilterParam = currentUrl.searchParams.get('filters');
        filters.forEach(filter => {
            const btn = document.createElement('a');
            btn.className = 'power-search-btn';
            btn.textContent = filter.label;
            const targetUrl = new URL(window.location.href);
            if (filter.param) {
                targetUrl.searchParams.set('filters', `ex1:"${filter.param}"`);
            } else {
                targetUrl.searchParams.delete('filters');
            }
            btn.href = targetUrl.toString();
            if (currentFilterParam === targetUrl.searchParams.get('filters')) {
                btn.classList.add('active');
            }
            group.appendChild(btn);
        });
        return group;
    }
    function parseQuery(query) {
        let base = query;
        let mode = 'smart';
        const siteMatch = base.match(/\s*site:(\S+)/i);
        const site = siteMatch ? siteMatch[1] : null;
        base = base.replace(/\s*site:\S+/ig, '');
        const filetypeMatch = base.match(/\s*filetype:(\S+)/i);
        const filetype = filetypeMatch ? filetypeMatch[1] : null;
        base = base.replace(/\s*filetype:\S+/ig, '');
        if (base.trim().startsWith('intitle:')) {
            mode = 'intitle';
            base = base.replace(/^intitle:/i, '');
        } else if (base.trim().startsWith('"') && base.trim().endsWith('"')) {
            mode = 'exact';
            base = base.replace(/^"|"$/g, '');
        }
        return { base: base.trim(), mode, filetype, site };
    }
    function generateQuery(parts) {
        let query = parts.base;
        switch (parts.mode) {
            case 'exact': query = `"${parts.base}"`; break;
            case 'intitle': query = `intitle:${parts.base}`; break;
        }
        if (parts.filetype) query += ` filetype:${parts.filetype}`;
        if (parts.site) query += ` site:${parts.site}`;
        return query.trim();
    }
    function createPowerButton(label, query) {
        const btn = document.createElement('a');
        btn.className = 'power-search-btn';
        btn.textContent = label;
        btn.href = `/search?q=${encodeURIComponent(query)}`;
        return btn;
    }
    function createGroup(labelText) {
        const group = document.createElement('div');
        group.className = 'power-search-group';
        if (labelText) {
            const label = document.createElement('span');
            label.className = 'power-search-group-label';
            label.textContent = labelText;
            group.appendChild(label);
        }
        return group;
    }
    function handleBingInfiniteScroll(resultsContainer) {
        if (!resultsContainer) return;
        let isLoading = false;
        const getNextPageUrl = () => document.querySelector('#b_pag a.sb_pagN')?.href;
        const loader = document.createElement('div');
        loader.id = 'infinite-scroll-loader';
        resultsContainer.insertAdjacentElement('afterend', loader);
        const observer = new IntersectionObserver(async (entries) => {
            if (!entries?.[0]?.isIntersecting || isLoading) return;
            isLoading = true;
            const nextPageUrl = getNextPageUrl();
            if (!nextPageUrl) {
                observer.disconnect();
                loader.textContent = '已加载全部结果';
                return;
            }
            loader.textContent = '正在加载更多结果...';
            try {
                const response = await fetch(nextPageUrl);
                const html = await response.text();
                const doc = new DOMParser().parseFromString(html, 'text/html');
                doc.querySelectorAll('#b_results > li').forEach(result => resultsContainer.appendChild(result));
                const oldPagination = document.getElementById('b_pag');
                if (oldPagination) oldPagination.innerHTML = doc.getElementById('b_pag')?.innerHTML || '';
            } catch (error) {
                loader.textContent = '加载失败';
                console.error('Infinite scroll failed:', error);
            } finally {
                isLoading = false;
                if (loader.textContent === '正在加载更多结果...') loader.textContent = '';
            }
        }, { threshold: 1.0 });
        observer.observe(loader);
    }
    function injectUI() {
        if (document.getElementById('userscript-ui-container')) return;
        const container = document.createElement('div');
        container.id = 'userscript-ui-container';
        container.innerHTML = `<div id="manage-blocklist-btn" class="userscript-btn" title="管理屏蔽列表"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path d="M10 12.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5z" /><path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.022 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd" /></svg></div><div id="go-to-top-btn" class="userscript-btn" title="返回顶部"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M11.47 2.47a.75.75 0 011.06 0l7.5 7.5a.75.75 0 11-1.06 1.06l-6.22-6.22V21a.75.75 0 01-1.5 0V4.81l-6.22 6.22a.75.75 0 11-1.06-1.06l7.5-7.5z" clip-rule="evenodd" /></svg></div>`;
        document.body.appendChild(container);
        createBlocklistModal();
        document.getElementById('manage-blocklist-btn').addEventListener('click', populateAndShowModal);
    }
    function handleGoToTop() {
        const btn = document.getElementById('go-to-top-btn');
        if (!btn) return;
        window.addEventListener('scroll', () => {
            btn.classList.toggle('visible', window.scrollY > 100);
        });
        btn.addEventListener('click', () => {
            window.scrollTo({ top: 0, behavior: 'smooth' });
        });
    }
    function addParticleClickEffect() {
        const colors = ['#FF69B4', '#1E90FF', '#00CED1', '#FFD700', '#7CFC00', '#BA55D3', '#FF4500'];
        document.body.addEventListener('click', function(e) {
            if (e.target.closest('a, button, #go-to-top-btn, .block-site-btn, #manage-blocklist-btn, #blocklist-modal')) return;
            for (let i = 0; i < 25; i++) {
                const particle = document.createElement('div');
                particle.className = 'sparkle-particle';
                document.body.appendChild(particle);
                particle.style.left = `${e.clientX}px`;
                particle.style.top = `${e.clientY}px`;
                particle.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
                const angle = Math.random() * Math.PI * 2;
                const distance = Math.random() * 150 + 80;
                requestAnimationFrame(() => {
                    particle.style.transform = `translate(${Math.cos(angle) * distance}px, ${Math.sin(angle) * distance}px) scale(0)`;
                    particle.style.opacity = '0';
                });
                setTimeout(() => particle.remove(), 1500);
            }
        });
    }

    // --- [MODIFIED] 初始化逻辑,采用持续观察模式以增强稳定性 ---
    function initialize() {
        const hostname = window.location.hostname;
        if (hostname.includes('bing.com')) { document.body.dataset.host = 'bing'; }
        else if (hostname.includes('msn.cn')) { document.body.dataset.host = 'msn'; }

        injectUI();
        handleGoToTop();
        addParticleClickEffect();

        if (hostname.includes('bing.com')) {
            const runInitializationTasks = (container) => {
                if (container.dataset.initialized) return;
                container.dataset.initialized = 'true';

                applyBlocklist();
                injectBlockButtons();
                createPowerSearchToolbar();
                handleBingInfiniteScroll(container);

                const resultsObserver = new MutationObserver((mutationsList) => {
                    const blocklist = blocklistManager.getBlocklist();
                    for (const mutation of mutationsList) {
                        if (mutation.type === 'childList') {
                            mutation.addedNodes.forEach(node => {
                                if (node.nodeType === Node.ELEMENT_NODE) {
                                    if (node.matches('li.b_algo, li.b_ans')) {
                                        applyBlocklistToElement(node, blocklist);
                                        injectBlockButtonToElement(node);
                                    }
                                    node.querySelectorAll('li.b_algo, li.b_ans').forEach(result => {
                                        applyBlocklistToElement(result, blocklist);
                                        injectBlockButtonToElement(result);
                                    });
                                }
                            });
                        }
                    }
                });

                resultsObserver.observe(container, { childList: true, subtree: true });
            };

            const mainObserver = new MutationObserver(() => {
                const resultsContainer = document.getElementById('b_results');
                if (resultsContainer) {
                    runInitializationTasks(resultsContainer);
                }
            });

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

            const initialResultsContainer = document.getElementById('b_results');
            if (initialResultsContainer) {
                runInitializationTasks(initialResultsContainer);
            }
        }
    }

    // --- 启动脚本 ---
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }

})();