// ==UserScript== // @name 特别好用的搜索引擎跳转脚本 // @namespace http://tampermonkey.net/ // @license MIT // @version 1.5.0 // @description 搜索引擎相互跳转,对页面入侵性最小,置顶显示;并且还有复制后可以选择多种搜索引擎进行搜索的功能。(已完美修复 Cloudflare 盾卡死问题 & 增加高级选词弹窗配置) // @author Gemini // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @run-at document-start // @downloadURL https://update.greasyfork.icu/scripts/569195/%E7%89%B9%E5%88%AB%E5%A5%BD%E7%94%A8%E7%9A%84%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E%E8%B7%B3%E8%BD%AC%E8%84%9A%E6%9C%AC.user.js // @updateURL https://update.greasyfork.icu/scripts/569195/%E7%89%B9%E5%88%AB%E5%A5%BD%E7%94%A8%E7%9A%84%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E%E8%B7%B3%E8%BD%AC%E8%84%9A%E6%9C%AC.meta.js // ==/UserScript== (function() { 'use strict'; // ===================================================================== // 0. 安全防御突破:构建 Trusted Types Policy (免疫 YouTube 拦截) // ===================================================================== const gmPolicy = (function() { if (typeof window.trustedTypes !== 'undefined' && window.trustedTypes.createPolicy) { try { return window.trustedTypes.createPolicy('gm_search_policy', { createHTML: (s) => s }); } catch (e) { return { createHTML: (s) => s }; } } return { createHTML: (s) => s }; })(); // ===================================================================== // 1. 核心配置与引擎数据库 (Base64 源码修改区) // ===================================================================== const PLACEHOLDER_BASE64 = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzllOWU5ZSI+PHBhdGggZD0iTTEyIDJDNi40OCAyIDIgNi40OCAyIDEyczQuNDggMTAgMTAgMTAgMTAtNC40OCAxMC0xMFMxNy41MiAyIDEyIDJ6bS0xIDE3LjkzYy0zLjk1LS40OS03LTMuODUtNy03LjkzIDAtLjYyLjA4LTEuMjEuMjMtMS43OUw5IDE1djFjMCAxLjEgLjkgMiAyIDJ2MS45M3ptNi45LTEuNTRjLS41NC0xLjE3LTEuNjQtMi0yLjktMnYtMmMwLS41NS0uNDUtMS0xLTFoLTJ2LTIuNWMwLS4yOC4yMi0uNS41LS41aDRjLjU1IDAgMS0uNDUgMS0xVjhjMC0uNTUtLjQ1LTEtMS0xaC00Yy0uNTUgMC0xIC40NS0xIDF2My41aC0xdi0xLjVjMC0uNTUtLjQ1LTEtMS0xaC0xYy0uNTUgMC0xLS40NS0xLTFWNmgyYy41NSAwIDEtLjQ1IDEtMXYtLjQ2YzEuMDUtLjM0IDIuMTgtLjU0IDMuNC0uNTQgNS41MSAwIDEwIDQuNDkgMTAgMTB6Ii8+PC9zdmc+"; const DEFAULT_ENGINES = [ { id: 'baidu', name: '百度', domain: 'baidu.com', url: 'https://www.baidu.com/s?wd=%s', icon: '', enabled: true }, { id: 'google', name: 'Google', domain: 'google.com', url: 'https://www.google.com/search?q=%s', icon: '', enabled: true }, { id: 'bing', name: '必应', domain: 'bing.com', url: 'https://www.bing.com/search?q=%s', icon: '', enabled: true }, { id: 'bilibili', name: 'B站', domain: 'bilibili.com', url: 'https://search.bilibili.com/all?keyword=%s', icon: '', enabled: true }, { id: 'youtube', name: 'YouTube', domain: 'youtube.com', url: 'https://www.youtube.com/results?search_query=%s', icon: '', enabled: true }, { id: 'xiaohongshu', name: '小红书', domain: 'xiaohongshu.com', url: 'https://www.xiaohongshu.com/search_result?keyword=%s', icon: "data:image/svg+xml;charset=utf-8;base64,PHN2ZyB0PSIxNzczMjQwMjk4MDQ1IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjM4OTEiIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4Ij48cGF0aCBkPSJNMCAwbTI1NiAwbDUxMiAwcTI1NiAwIDI1NiAyNTZsMCA1MTJxMCAyNTYtMjU2IDI1NmwtNTEyIDBxLTI1NiAwLTI1Ni0yNTZsMC01MTJxMC0yNTYgMjU2LTI1NloiIGZpbGw9IiNGQTJDMTkiIHAtaWQ9IjM4OTIiPjwvcGF0aD48cGF0aCBkPSJNNDQ1LjgyNCA3NjYuMjkzMzMzYzQuMTgxMzMzLTkuMTMwNjY3IDcuNjgtMTcuMDY2NjY3IDExLjQ3NzMzMy0yNC43ODkzMzNhNTEyIDUxMiAwIDAgMCAyMy4yNTMzMzQtNDkuMjM3MzMzIDI5LjE0MTMzMyAyOS4xNDEzMzMgMCAwIDEgMzQuNzczMzMzLTIxLjcxNzMzNGMyMS45NzMzMzMgMS41Nzg2NjcgNDQuMDMyIDAuNDI2NjY3IDY2Ljk4NjY2NyAwLjQyNjY2N1YzODQuODUzMzMzYy0xNS40NDUzMzMgMC0zMC42NzczMzMtMC41NTQ2NjctNDUuNzgxMzM0IDAtMTAuNTM4NjY3IDAuNTU0NjY3LTE0LjI1MDY2Ny0yLjkwMTMzMy0xMy45MDkzMzMtMTQuMTY1MzMzIDAuNzI1MzMzLTI3LjA5MzMzMyAwLTU0LjI3MiAwLTgyLjYwMjY2N2gyMTQuMjI5MzMzdjY1LjcwNjY2N2MwIDMwLjg5MDY2NyAwIDMwLjg5MDY2Ny0yOS45NTIgMzAuODkwNjY3aC0zMC4xMjI2NjZ2Mjg1Ljk1Mmg2NS4wMjRjMjYuMTk3MzMzIDAgMjYuMTk3MzMzIDAgMjYuMTk3MzMzIDI3LjUydjU3LjY0MjY2NmMwIDcuOTc4NjY3LTEuOTYyNjY3IDEyLjAzMi0xMC42MjQgMTIuMDMyLTEwMS42NzQ2NjctMC4xNzA2NjctMjAzLjM5Mi0wLjI5ODY2Ny0zMDUuMDY2NjY3LTAuMjEzMzMzYTM3LjcxNzMzMyAzNy43MTczMzMgMCAwIDEtNi40ODUzMzMtMS4zNjUzMzMiIGZpbGw9IiNGRkZGRkYiIHAtaWQ9IjM4OTMiPjwvcGF0aD48cGF0aCBkPSJNNDg2LjM1NzMzMyA1NTkuMTQ2NjY3Yy0xMy4zOTczMzMgMjcuNjA1MzMzLTI1LjE3MzMzMyA1Mi4yNjY2NjctMzcuNTQ2NjY2IDc2LjUwMTMzM2ExMS42MDUzMzMgMTEuNjA1MzMzIDAgMCAxLTguOTYgNC41MjI2NjdjLTI5Ljc4MTMzMyAwLTU5LjczMzMzMyAxLjE1Mi04OS40MjkzMzQtMS4wNjY2NjctMjkuNzM4NjY3LTIuMjE4NjY3LTQxLjY0MjY2Ny0yMS4zMzMzMzMtMjkuNzgxMzMzLTUwLjUxNzMzMyAxMy40ODI2NjctMzMuNjY0IDI5LjczODY2Ny00NC44LTk5LjJsMy40MTMzMzMtOC44MzJjLTEyLjAzMiAwLTIyLjU3MDY2NyAwLjI5ODY2Ny0zMy4xMDkzMzMgMC04LjU3NiAwLjEyOC0xNy4xNTItMC42ODI2NjctMjUuNTE0NjY3LTIuMzQ2NjY3YTMwLjAzNzczMzMgMzAuMDM3MzMzIDAgMCAxLTI1LjcyOC0zMy41MzYgMzAuNzIgMzAuNzIgMCAwIDEgMi45MDEzMzQtMTAuMTEyYzE4LjA0OC00My4zNDkzMzMgMzguMjcyLTg1Ljg0NTMzMyA1Ny43NzA2NjYtMTI4LjU1NDY2NyA2LjM1NzMzMy0xMy45OTQ2NjcgMTMuMDEzMzMzLTI3Ljc3NiAyMC4xODEzMzQtNDEuMzg2NjY2IDEuODM0NjY3LTMuNTQxMzMzIDYuMTAxMzMzLTguMDY0IDkuMzg2NjY2LTguMTkyIDI3Ljg2MTMzMy0wLjY4MjY2NyA1NS44MDgtMC4zNDEzMzMgODYuMTg2NjY3LTAuMzQxMzM0LTIuNjQ1MzMzIDYuNzg0LTQuMTM4NjY3IDExLjM5Mi02LjE0NCAxNS43MDEzMzQtMTcuMDY2NjY3IDM1LjcxMi0zNC4xNzYgNzEuMzgxMzMzLTUxLjM3MDY2NyAxMDYuOTY1MzMzLTMuNDU2IDcuMjEwNjY3LTcuNjggMTQuNzIgNS4yOTA2NjcgMjAuMjI0IDMuNDEzMzMzLTE4LjU2IDE3LjQwOC0xNS4xODkzMzMgMjkuNjEwNjY3LTE1LjE4OTMzM2g3MC40Yy0yLjk0NCA3LjA0LTQuODY0IDExLjk0NjY2Ny02Ljk5NzMzNCAxNi41OTczMzMtMjEuNzYgNDUuNDQtNDMuODYxMzMzIDkwLjUzODY2Ny02NS4yOCAxMzUuOTM2LTguNzg5MzMzIDE4LjQ3NDY2Ny01Ljg0NTMzMyAyMi45OTczMzMgMTQuNjM0NjY3IDIzLjE2OCAxMC41Mzg2NjctMC4yOTg2NjcgMjEuMjA1MzMzLTAuMzQxMzMzIDM1LjI4NTMzMy0wLjM0MTMzM20tMzguMzE0NjY2IDExMS44NzJjLTE2LjUxMiAzMy4xMDkzMzMtMzEuMjc0NjY3IDYyLjg5MDY2Ny00Ni4zNzg2NjcgOTIuNTAxMzMzYTEwLjI0IDEwLjI0IDAgMCAxLTcuNjggNC4yNjY2NjdjLTQwLjQ5MDY2Ny0wLjQyNjY2Ny04MS4wNjY2NjctMS4xOTQ2NjctMTIxLjY4NTMzMy0yLjI2MTMzNGE3OS4zMTczMzMgNzkuMzE3MzMzIDAgMCAxLTE2LjI5ODY2Ny00LjI2NjY2NmwyMi42OTg2NjctNDUuOTA5MzM0YzcuMzgxMzMzLTE1LjE4OTMzMyAxNC41OTItMzAuMjkzMzMzIDIyLjU3MDY2Ni00NC43MTQ2NjZhMTMuNjUzMzMzIDEzLjY1MzMzMyAwIDAgMSA5LjcyOC02LjRjMzcuMjA1MzMzIDEuODM0NjY3IDc0LjQxMDY2NyA0LjQzNzMzMyAxMTEuNjU4NjY3IDYuNjk4NjY2IDcuNDI0IDAuMzg0IDE0LjUwNjY2NyAwLjA4NTMzMyAyNS4zODY2NjcgMC4wODUzMzQiIGZpbGw9IiNGRkZGRkYiIHAtaWQ9IjM4OTQiPjwvcGF0aD48L3N2Zz4=", enabled: true }, { id: 'douyin', name: '抖音', domain: 'douyin.com', url: 'https://www.douyin.com/search/%s', icon: '', enabled: true }, { id: 'weibo', name: '微博', domain: 'weibo.com', url: 'https://s.weibo.com/weibo?q=%s', icon: '', enabled: true } ]; const DEFAULT_CONFIG = { height: 56, fontSize: 15, gap: 20, theme: 'auto', alwaysPin: false, showSelectionPreview: true, popupTrigger: 'auto', engines: DEFAULT_ENGINES }; let userConfig = GM_getValue('gm_search_config', DEFAULT_CONFIG); if (!userConfig.engines || userConfig.engines.length === 0) userConfig.engines = DEFAULT_ENGINES; userConfig.engines.forEach(eng => { const defEng = DEFAULT_ENGINES.find(d => d.id === eng.id); if (defEng) eng.icon = defEng.icon; }); userConfig = { ...DEFAULT_CONFIG, ...userConfig }; // ===================================================================== // 2. 纯净 DOM 构建函数 // ===================================================================== function el(tag, props, children) { const e = document.createElement(tag); if (props) { for (let k in props) { if (k === 'className') e.className = props[k]; else if (k === 'dataset') { for (let d in props[k]) e.dataset[d] = props[k][d]; } else if (k === 'style') { for (let s in props[k]) e.style[s] = props[k][s]; } else e[k] = props[k]; } } if (children) { for (let i = 0; i < children.length; i++) { const c = children[i]; if (typeof c === 'string' || typeof c === 'number') e.appendChild(document.createTextNode(String(c))); else if (c instanceof Node) e.appendChild(c); } } return e; } // ===================================================================== // 3. 动态场景嗅探 & 关键词提取 // ===================================================================== function checkIsSearchPage() { const host = window.location.hostname, path = window.location.pathname; if (host.includes('baidu.com') && path.includes('/s')) return true; if (host.includes('google.com') && path.includes('/search')) return true; if (host.includes('bing.com') && path.includes('/search')) return true; if (host.includes('search.bilibili.com')) return true; if (host.includes('youtube.com') && path.includes('/results')) return true; if (host.includes('xiaohongshu.com') && path.includes('/search_result')) return true; if (host.includes('douyin.com') && path.includes('/search')) return true; if (host.includes('weibo.com') && path.includes('/weibo')) return true; for (let eng of userConfig.engines) { if (eng.enabled && host.includes(eng.domain) && window.location.search) { try { const queryParams = new URLSearchParams(new URL(eng.url).search); for (let [key, val] of queryParams.entries()) { if (val.includes('%s') && new URLSearchParams(window.location.search).has(key)) return true; } } catch(e) {} } } return false; } let isEngineSearchPage = checkIsSearchPage(); function getOriginalKeyword() { const url = new URL(window.location.href), host = url.hostname; let kw = ''; for (let eng of userConfig.engines) { if (host.includes(eng.domain)) { try { const queryParams = new URLSearchParams(new URL(eng.url).search); for (let [key, val] of queryParams.entries()) { if (val.includes('%s')) { kw = url.searchParams.get(key); if (kw) break; } } } catch(e) {} } if (kw) break; } if (!kw && host.includes('douyin.com')) { const pathParts = url.pathname.split('/'); if (pathParts[1] === 'search' && pathParts[2]) kw = decodeURIComponent(pathParts[2]); } if (!kw) { const inputs = document.querySelectorAll('input[type="text"], input[type="search"]'); for (let input of inputs) { if (input.value && input.value.trim() !== '') { kw = input.value.trim(); break; } } } return kw ? kw.trim() : ''; } // ===================================================================== // 4. 智能主题感知系统 // ===================================================================== function getPageLuminance() { const getBg = (elem) => { let bg = window.getComputedStyle(elem).backgroundColor; return (bg === 'rgba(0, 0, 0, 0)' || bg === 'transparent') ? null : bg; }; let bg = getBg(document.body) || getBg(document.documentElement); if (!bg) return 'light'; let match = bg.match(/\d+/g); if (match && match.length >= 3) { let r = parseInt(match[0]), g = parseInt(match[1]), b = parseInt(match[2]); return ((r * 299 + g * 587 + b * 114) / 1000) < 128 ? 'dark' : 'light'; } return 'light'; } function applySmartTheme() { let theme = userConfig.theme; if (theme === 'auto') theme = getPageLuminance(); document.documentElement.setAttribute('data-gm-theme', theme); } // ===================================================================== // 5. CSS 隔离与各大站基础适配 // ===================================================================== const SITE_RULES = { 'baidu.com': `#head, #s_tab { top: var(--gm-offset) !important; transition: top 0.3s ease !important; }`, 'google.com': `html.gm-search-page.gm-scrolled #searchform, html.gm-search-page.gm-scrolled .sfbg, html.gm-search-page.gm-scrolled #hdtb { margin-top: var(--gm-offset) !important; transition: margin-top 0.3s ease !important; }`, 'bing.com': `#b_header { position: sticky !important; top: var(--gm-offset) !important; z-index: 10000 !important; transition: top 0.3s ease, padding 0.3s ease !important; background-color: var(--gm-bg) !important; } html.gm-scrolled #b_header nav, html.gm-scrolled #b_header .b_scopebar, html.gm-scrolled #b_header #b_header_nav { display: none !important; } html.gm-scrolled #b_header { padding-top: 12px !important; padding-bottom: 12px !important; height: auto !important; min-height: 0 !important; }`, 'weibo.com': ` html.gm-search-page #searchapps, html.gm-search-page [class*="Nav_wrap_"], html.gm-search-page [class*="Nav_panel_"], html.gm-search-page #weibo_top_public { top: var(--gm-offset) !important; transition: top 0.3s ease !important; } html.gm-search-page #searchapps { position: sticky !important; z-index: 9999 !important; } `, 'xiaohongshu.com': ` html.gm-search-page #global > .header-container { top: var(--gm-offset) !important; transition: top 0.3s ease !important; z-index: 9999 !important; } html.gm-search-page .side-bar { top: calc(var(--gm-offset) + 72px) !important; transition: top 0.3s ease !important; } html.gm-search-page .main-container { padding-top: var(--gm-offset) !important; transition: padding-top 0.3s ease !important; } html.gm-search-page .reds-sticky-box, html.gm-search-page .tag-search-page-sticky, html.gm-search-page .search-layout__middle { display: none !important; } `, 'youtube.com': ` html.gm-search-page #masthead-container { top: var(--gm-offset) !important; transform: none !important; transition: top 0.3s ease !important; } html.gm-search-page ytd-page-manager, html.gm-search-page #page-manager { margin-top: calc(56px + var(--gm-offset)) !important; transform: none !important; transition: margin-top 0.3s ease !important; } html.gm-search-page tp-yt-app-drawer { top: calc(56px + var(--gm-offset)) !important; transition: top 0.3s ease !important; } html.gm-search-page #chips-wrapper, html.gm-search-page ytd-feed-filter-chip-bar-renderer { top: calc(56px + var(--gm-offset)) !important; transition: top 0.3s ease !important; } `, 'douyin.com': ` html.gm-search-page #douyin-header { top: var(--gm-offset) !important; transition: top 0.3s ease !important; z-index: 9999 !important; } html.gm-search-page a[href="//www.douyin.com/"] { transform: translateY(var(--gm-offset)) !important; transition: transform 0.3s ease !important; z-index: 10000 !important; display: inline-block; } html.gm-search-page #search-toolbar-container { position: sticky !important; top: calc(56px + var(--gm-offset)) !important; transition: top 0.3s ease !important; z-index: 9998 !important; } html.gm-search-page .YDoaql1z { display: none !important; } html.gm-search-page .hDLEI3Iz { margin-top: 0 !important; padding-top: 0 !important; } `, 'bilibili.com': `.bili-header__bar, #biliMainHeader { position: fixed !important; top: var(--gm-offset) !important; left: 0 !important; width: 100% !important; z-index: 10000 !important; background-color: var(--gm-bg) !important; box-shadow: 0 2px 4px rgba(0,0,0,0.08) !important; transition: top 0.3s ease !important; transform: none !important; animation: none !important; } .bili-header { min-height: 64px !important; } .search-sticky-header, [class*="sticky-header"] { display: none !important; } .bili-header__bar .center-search-container, #biliMainHeader .center-search-container { display: flex !important; opacity: 1 !important; visibility: visible !important; pointer-events: auto !important; flex: 1 1 auto !important; width: 100% !important; max-width: 500px !important; margin: 0 auto !important; } .bili-header__bar .center-search__bar, #biliMainHeader .center-search__bar { width: 100% !important; max-width: 500px !important; margin: 0 auto !important; } .center-search-container form, .center-search-container #nav-searchform { display: flex !important; flex: 1 !important; width: 100% !important; } .bili-header__bar .right-entry-text, #biliMainHeader .right-entry-text { display: inline-block !important; } .bili-header__bar .left-entry ~ div:not(.center-search-container):not(.right-entry), #biliMainHeader .left-entry ~ div:not(.center-search-container):not(.right-entry) { display: none !important; } .center-search-container .nav-search-keyword { display: none !important; } .center-search-container input::-webkit-input-placeholder, .center-search-container input::-moz-placeholder, .center-search-container input:-ms-input-placeholder, .center-search-container input::placeholder { color: transparent !important; text-shadow: none !important; } .center-search-container input { color: var(--gm-text) !important; opacity: 1 !important; -webkit-text-fill-color: var(--gm-text) !important; } .search-header .search-input { display: none !important; } .search-header { background: transparent !important; min-height: 0 !important; height: auto !important; padding-top: 20px !important; padding-bottom: 5px !important; } .bili-header__banner, .download-entry, .download-client-trigger, .bili-header .left-entry .v-popover-wrap:has(.download-client-trigger), .bili-header a[href*="//app.bilibili.com"] { display: none !important; }` }; let isBlockVisible = isEngineSearchPage; let isSelectionMode = false; let lastScrollTop = 0; const currentDomain = window.location.hostname; let styleEl = null; let cssText = ` :root { --gm-config-height: ${userConfig.height}px; --gm-config-fontsize: ${userConfig.fontSize}px; --gm-config-gap: ${userConfig.gap}px; --gm-offset: 0px; --gm-bg: #ffffff; --gm-text: #222222; --gm-hover: #f5f5f5; --gm-active: #e8e8e8; --gm-border: #f0f0f0; --gm-shadow: 0 1px 3px rgba(0,0,0,0.04); --gm-pill-bg: #e6f4ff; --gm-pill-text: #1677ff; --gm-pill-border: #91caff; } html[data-gm-theme="dark"], @media (prefers-color-scheme: dark) { html[data-gm-theme="auto"] { --gm-bg: #1e1e20; --gm-text: #e0e0e0; --gm-hover: #2b2b2d; --gm-active: #3a3a3c; --gm-border: #2c2c2e; --gm-shadow: 0 1px 3px rgba(0,0,0,0.5); --gm-pill-bg: rgba(22, 119, 255, 0.15); --gm-pill-text: #4096ff; --gm-pill-border: rgba(22, 119, 255, 0.3); }} html.gm-search-page { margin-top: var(--gm-offset) !important; box-sizing: border-box !important; transition: margin-top 0.3s ease !important; } #custom-fixed-top-block { position: fixed; top: -100px; left: 0; width: 100%; height: var(--gm-config-height); background-color: var(--gm-bg); border-bottom: 1px solid var(--gm-border); display: flex; align-items: center; justify-content: space-between; padding: 0 20px; z-index: 2147483647 !important; pointer-events: auto; transition: top 0.3s cubic-bezier(0.2, 0.8, 0.2, 1), background-color 0.3s, box-shadow 0.3s, border 0.3s; box-shadow: var(--gm-shadow); box-sizing: border-box; user-select: none; } .gm-left-zone, .gm-right-zone { flex: 1; display: flex; align-items: center; min-width: 0; } .gm-left-zone { justify-content: flex-start; } .gm-engines-wrapper { flex: 0 0 auto; display: flex; align-items: center; gap: var(--gm-config-gap); } #gm-selection-preview { display: flex; align-items: center; background: transparent; color: rgb(22, 119, 255); border: none; padding: 0; font-size: 13px; max-width: 100%; box-sizing: border-box; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; opacity: 0; transform: translateX(-10px); pointer-events: none; transition: opacity 0.3s ease, transform 0.3s ease; } #custom-fixed-top-block.gm-selection-mode #gm-selection-preview { opacity: 1; transform: translateX(0); } .gm-engine-btn { display: flex; align-items: center; gap: 6px; padding: calc(var(--gm-config-height) * 0.1) 14px; border-radius: 6px; color: var(--gm-text); font-size: var(--gm-config-fontsize); font-weight: 500; cursor: pointer; transition: background 0.2s, transform 0.1s; } .gm-engine-btn:hover { background: var(--gm-hover); } #custom-fixed-top-block:not(.gm-selection-mode) .gm-engine-btn.gm-active { font-weight: bold; } .gm-engine-btn img { width: 1.1em !important; height: 1.1em !important; border-radius: 4px !important; object-fit: contain !important; display: inline-block !important; background-color: transparent !important; pointer-events: none; } /* GUI 绝对防污染隔离 */ #gm-settings-panel { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 340px; background: var(--gm-bg); border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); z-index: 2147483648; display: none; flex-direction: column; border: 1px solid var(--gm-border); color: var(--gm-text); overflow: hidden; text-align: left !important; } #gm-settings-panel, #gm-settings-panel * { box-sizing: border-box !important; font-family: system-ui, -apple-system, sans-serif !important; letter-spacing: normal !important; word-spacing: normal !important; line-height: 1.5 !important; text-transform: none !important; text-shadow: none !important; white-space: normal !important; word-break: normal !important; } #gm-settings-panel .gm-settings-header { padding: 14px 18px; border-bottom: 1px solid var(--gm-border); display: flex; justify-content: space-between; align-items: center; cursor: grab; background: var(--gm-hover); } #gm-settings-panel .gm-settings-header:active { cursor: grabbing; } #gm-settings-panel .gm-settings-header span:first-child { font-size: 16px !important; font-weight: bold !important; color: var(--gm-text) !important; } #gm-settings-panel .gm-settings-close { cursor: pointer; font-size: 20px !important; font-weight: normal !important; color: #999 !important; } #gm-settings-panel .gm-settings-close:hover { color: #ff4d4f !important; } #gm-settings-panel .gm-tabs { display: flex; border-bottom: 1px solid var(--gm-border); } #gm-settings-panel .gm-tab { flex: 1; text-align: center !important; padding: 12px 0; cursor: pointer; font-size: 14px !important; color: var(--gm-text); opacity: 0.6; transition: 0.2s; font-weight: normal !important; } #gm-settings-panel .gm-tab.active { opacity: 1; font-weight: bold !important; color: #1677ff !important; border-bottom: 2px solid #1677ff !important; } #gm-settings-panel .gm-tab-content { display: none; flex-direction: column; gap: 16px; padding: 18px; max-height: 400px; overflow-y: auto; } #gm-settings-panel .gm-tab-content.active { display: flex; } #gm-settings-panel .gm-setting-item { display: flex; flex-direction: column; gap: 8px; } #gm-settings-panel .gm-setting-item-row { display: flex; flex-direction: row; justify-content: space-between; align-items: center; } #gm-settings-panel .gm-setting-label { display: flex; justify-content: space-between; align-items: center; width: 100%; } #gm-settings-panel .gm-setting-label span { font-size: 14px !important; color: var(--gm-text) !important; font-weight: normal !important;} #gm-settings-panel .gm-setting-value { color: #1677ff !important; font-weight: bold !important; font-size: 14px !important; } #gm-settings-panel input[type=range] { -webkit-appearance: none; width: 100%; height: 6px !important; background: #e5e5e5 !important; border-radius: 3px !important; outline: none; margin: 0 !important; } #gm-settings-panel input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 16px !important; height: 16px !important; border-radius: 50% !important; background: #1677ff !important; cursor: pointer; border: 2px solid #fff !important; box-shadow: 0 1px 3px rgba(0,0,0,0.3) !important; } #gm-settings-panel select, #gm-settings-panel input[type=text], #gm-settings-panel textarea { width: 100%; padding: 8px !important; border-radius: 6px !important; border: 1px solid var(--gm-border) !important; background: var(--gm-bg) !important; color: var(--gm-text) !important; font-size: 13px !important; outline: none; box-sizing: border-box; } #gm-settings-panel input[type=text]:focus, #gm-settings-panel textarea:focus { border-color: #1677ff !important; } #gm-settings-panel textarea { resize: vertical; min-height: 60px; } #gm-settings-panel .gm-switch { position: relative; display: inline-block; width: 40px !important; height: 22px !important; flex-shrink: 0; } #gm-settings-panel .gm-switch input { opacity: 0; width: 0; height: 0; margin: 0; } #gm-settings-panel .gm-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .3s; border-radius: 22px !important; } #gm-settings-panel .gm-slider:before { position: absolute; content: ""; height: 18px !important; width: 18px !important; left: 2px !important; bottom: 2px !important; background-color: white; transition: .3s; border-radius: 50% !important; box-shadow: 0 1px 2px rgba(0,0,0,0.2) !important; } #gm-settings-panel input:checked + .gm-slider { background-color: #1677ff !important; } #gm-settings-panel input:checked + .gm-slider:before { transform: translateX(18px) !important; } #gm-settings-panel .gm-engine-list-item { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; background: var(--gm-hover); border-radius: 8px; margin-bottom: 8px; } #gm-settings-panel .gm-engine-list-item.disabled { opacity: 0.5; filter: grayscale(100%); } #gm-settings-panel .gm-engine-info { display: flex; align-items: center; gap: 8px; flex: 1; overflow: hidden; } #gm-settings-panel .gm-engine-info img { width: 18px !important; height: 18px !important; border-radius: 4px !important; flex-shrink: 0; } #gm-settings-panel .gm-engine-info span { font-size: 14px !important; font-weight: 500 !important; white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; } #gm-settings-panel .gm-engine-actions { display: flex; gap: 4px; } #gm-settings-panel .gm-action-btn { padding: 4px; cursor: pointer; font-size: 14px !important; opacity: 0.7; transition: 0.2s; border-radius: 4px; user-select: none; } #gm-settings-panel .gm-action-btn:hover { opacity: 1; background: rgba(0,0,0,0.05); } #gm-settings-panel .gm-primary-btn { padding: 10px; background: #1677ff; color: #fff !important; border: none; border-radius: 8px; cursor: pointer; font-weight: bold !important; font-size: 14px !important; text-align: center; } #gm-settings-panel .gm-primary-btn:hover { background: #4096ff; } #gm-settings-panel .gm-secondary-btn { padding: 10px; background: var(--gm-hover); color: var(--gm-text) !important; border: none; border-radius: 8px; cursor: pointer; font-size: 14px !important; text-align: center; } `; function injectDynamicCSS() { let dynamicRules = ''; if (isEngineSearchPage) { document.documentElement.classList.add('gm-search-page'); document.documentElement.style.setProperty('--gm-offset', userConfig.height + 'px'); for (const [domain, ruleCSS] of Object.entries(SITE_RULES)) { if (currentDomain.includes(domain)) { dynamicRules += `\n${ruleCSS}`; break; } } } else { document.documentElement.classList.remove('gm-search-page'); document.documentElement.style.setProperty('--gm-offset', '0px'); } if(styleEl) styleEl.textContent = cssText + dynamicRules; } // =====================================================================     // 🚀 安全路由系统:精准特征cloudflare识别 (融合底层网络指纹,绝对防卡死)     // =====================================================================     let __gm_is_shield_locked = false;     function isShieldPage() {         // 1. 白名单免疫         if (isEngineSearchPage) return false;         if (__gm_is_shield_locked) return true;         // 2. 高危标题嗅探         const t = document.title || '';         if (/^(Just a moment|Checking your browser|Attention Required|请稍候|安全验证)/i.test(t.trim())) {             __gm_is_shield_locked = true;             return true;         }         // 🌟 3. 最致命的网络特征底层嗅探 (核心修复)         // 真正的 CF 拦截页必定会加载 challenge-platform 脚本。         // 用这个路径判断,既能 100% 抓出伪装的 CF 盾,又绝对不会误杀带验证码的普通网页!         if (document.querySelector('script[src*="/cdn-cgi/challenge-platform/"]')) {             __gm_is_shield_locked = true;             return true;         }         // 4. 专属防御容器 DOM 特征         const shieldSelectors = '#challenge-running, #cf-wrapper, .cf-browser-verification, #challenge-stage';         if (document.querySelector(shieldSelectors)) {             __gm_is_shield_locked = true;             return true;         }         // 5. 极简 DOM + 底部水印兜底         if (document.body && document.querySelectorAll('*').length < 150) {             const text = document.body.innerText || '';             if (text.includes('Ray ID:') && text.includes('Cloudflare')) {                 __gm_is_shield_locked = true;                 return true;             }         }         return false;     } function bootScript() { if (!styleEl && (document.head || document.documentElement)) { styleEl = document.createElement('style'); styleEl.id = 'gm-search-style'; (document.head || document.documentElement).appendChild(styleEl); injectDynamicCSS(); } } if (isEngineSearchPage) { bootScript(); } else { document.addEventListener('DOMContentLoaded', () => { if (isShieldPage()) return; bootScript(); }); } function updateBarVisibility() { const topBlock = document.getElementById('custom-fixed-top-block'); if (!topBlock) return; if (isSelectionMode) { topBlock.style.top = '0px'; topBlock.classList.add('gm-selection-mode'); if (isEngineSearchPage) document.documentElement.style.setProperty('--gm-offset', userConfig.height + 'px'); return; } else { topBlock.style.top = `-${userConfig.height + 20}px`; topBlock.classList.remove('gm-selection-mode'); } if (!isEngineSearchPage) return; if (userConfig.alwaysPin) { isBlockVisible = true; topBlock.style.top = '0px'; document.documentElement.style.setProperty('--gm-offset', userConfig.height + 'px'); return; } if (isBlockVisible) { topBlock.style.top = '0px'; document.documentElement.style.setProperty('--gm-offset', userConfig.height + 'px'); } else { topBlock.style.top = `-${userConfig.height + 20}px`; document.documentElement.style.setProperty('--gm-offset', '0px'); } } function renderTopBarEngines() { const wrapper = document.querySelector('.gm-engines-wrapper'); if (!wrapper) return; wrapper.textContent = ''; const activeEngines = userConfig.engines.filter(e => e.enabled !== false); activeEngines.forEach(engine => { let finalIcon = engine.icon ? engine.icon.trim() : ''; if (finalIcon.startsWith(' 100 && !finalIcon.startsWith('http') && !finalIcon.startsWith('data:')) { finalIcon = `data:image/svg+xml;base64,${finalIcon}`; } const img = el('img', { src: finalIcon }); img.onerror = function() { this.src = PLACEHOLDER_BASE64; }; const span = el('span', {}, [engine.name]); const isActive = isEngineSearchPage && currentDomain.includes(engine.domain); const btn = el('div', { className: 'gm-engine-btn' + (isActive ? ' gm-active' : '') }, [img, span]); btn.addEventListener('mousedown', (e) => e.preventDefault()); btn.addEventListener('click', () => { const selText = window.getSelection().toString().trim(); const kw = selText || getOriginalKeyword(); if (kw) { const targetUrl = engine.url.replace('%s', encodeURIComponent(kw)); if (selText) { window.open(targetUrl, '_blank'); window.getSelection().removeAllRanges(); } else { window.location.href = targetUrl; } } else { window.location.href = `https://www.${engine.domain}`; } }); wrapper.appendChild(btn); }); } // ===================================================================== // 6. GUI 设置面板构建与逻辑 (安全延迟挂载) // ===================================================================== document.addEventListener('DOMContentLoaded', () => { if (typeof isShieldPage === 'function' && isShieldPage()) return; applySmartTheme(); const selectionPreview = el('div', { id: 'gm-selection-preview' }); const leftZone = el('div', { className: 'gm-left-zone' }, [selectionPreview]); const enginesWrapper = el('div', { className: 'gm-engines-wrapper' }); const rightZone = el('div', { className: 'gm-right-zone' }); const topBlock = el('div', { id: 'custom-fixed-top-block' }, [leftZone, enginesWrapper, rightZone]); document.documentElement.appendChild(topBlock); renderTopBarEngines(); updateBarVisibility(); // ===================================================================== // 🚀 核心逻辑升级:保持选区高亮,精准判断点击范围 // ===================================================================== let selectionTimeout; let pendingSelectionText = ''; let lastSelectionTime = 0; let savedRange = null; // 保存选区对象,用于获取高亮文本的精确物理坐标 const triggerSelectionPopup = (text) => { if (!text) { isSelectionMode = false; updateBarVisibility(); return; } if (userConfig.popupTrigger !== 'none') { isSelectionMode = true; if (userConfig.showSelectionPreview) { selectionPreview.textContent = `🔍 搜索: "${text}"`; selectionPreview.style.display = 'flex'; } else { selectionPreview.style.display = 'none'; } updateBarVisibility(); } }; // 判断鼠标点击的坐标是否在“蓝色的高亮矩形”范围内 const isClickInRects = (e, rects) => { for (let i = 0; i < rects.length; i++) { const r = rects[i]; // 加 2px 的容错边缘,提升点击手感 if (e.clientX >= r.left - 2 && e.clientX <= r.right + 2 && e.clientY >= r.top - 2 && e.clientY <= r.bottom + 2) { return true; } } return false; }; document.addEventListener('selectionchange', () => { clearTimeout(selectionTimeout); selectionTimeout = setTimeout(() => { const sel = window.getSelection(); const text = sel.toString().trim(); pendingSelectionText = text; lastSelectionTime = Date.now(); if (!text) { savedRange = null; triggerSelectionPopup(''); return; } if (sel.rangeCount > 0) { savedRange = sel.getRangeAt(0).cloneRange(); // 备份选区 } if (userConfig.popupTrigger === 'auto') { triggerSelectionPopup(text); } }, 80); }); // 🌟 魔法步骤 1:拦截 mousedown,防止浏览器清除蓝色高亮 document.addEventListener('mousedown', (e) => { if (e.button !== 0 || userConfig.popupTrigger !== 'click' || !savedRange) return; if (e.target.closest('#custom-fixed-top-block') || e.target.closest('#gm-settings-panel')) return; // 如果鼠标正好点在蓝色高亮区域内 if (isClickInRects(e, savedRange.getClientRects())) { e.preventDefault(); // 强行阻止浏览器清除选中状态! } }); // 🌟 魔法步骤 2:在鼠标抬起时,执行弹窗 document.addEventListener('mouseup', (e) => { if (e.button !== 0 || userConfig.popupTrigger !== 'click' || !pendingSelectionText || !savedRange) return; if (e.target.closest('#custom-fixed-top-block') || e.target.closest('#gm-settings-panel')) return; if (Date.now() - lastSelectionTime > 150) { // 判断:只有点在文字上了才触发面板,点在其他空白处则立刻收起面板 if (isClickInRects(e, savedRange.getClientRects())) { triggerSelectionPopup(pendingSelectionText); } else { triggerSelectionPopup(''); } } }); document.addEventListener('mouseup', (e) => { if (e.button !== 0) return; if (userConfig.popupTrigger !== 'click' || !pendingSelectionText) return; if (e.target.closest('#custom-fixed-top-block') || e.target.closest('#gm-settings-panel')) return; if (Date.now() - lastSelectionTime > 150) { // 🌟 触发面板前,先开启 300 毫秒的“免疫护盾” // 防止因为本次点击导致浏览器取消文字选中,从而触发 selectionchange 清空面板 ignoreClearEventUntil = Date.now() + 300; triggerSelectionPopup(pendingSelectionText); } }); let lastUrl = location.href; setInterval(() => { if (styleEl && typeof styleEl !== 'undefined' && !document.contains(styleEl)) { (document.head || document.documentElement).appendChild(styleEl); } if (typeof topBlock !== 'undefined' && !document.getElementById('custom-fixed-top-block')) { document.documentElement.appendChild(topBlock); } if (typeof panel !== 'undefined' && !document.getElementById('gm-settings-panel')) { document.documentElement.appendChild(panel); } if (isEngineSearchPage && !document.documentElement.classList.contains('gm-search-page')) { document.documentElement.classList.add('gm-search-page'); document.documentElement.style.setProperty('--gm-offset', userConfig.height + 'px'); } if (location.href !== lastUrl) { lastUrl = location.href; const newIsSearchPage = checkIsSearchPage(); if (newIsSearchPage !== isEngineSearchPage) { isEngineSearchPage = newIsSearchPage; injectDynamicCSS(); } updateBarVisibility(); renderTopBarEngines(); } }, 500); window.addEventListener('scroll', () => { if (!isEngineSearchPage) return; let currentScroll = window.pageYOffset || document.documentElement.scrollTop; if (currentScroll > 10) document.documentElement.classList.add('gm-scrolled'); else document.documentElement.classList.remove('gm-scrolled'); if (userConfig.alwaysPin || isSelectionMode) { lastScrollTop = currentScroll <= 0 ? 0 : currentScroll; return; } if (currentScroll > lastScrollTop && currentScroll > 50) { isBlockVisible = false; } else if (currentScroll < lastScrollTop) { isBlockVisible = true; } updateBarVisibility(); lastScrollTop = currentScroll <= 0 ? 0 : currentScroll; }, { passive: true }); const themeObserver = new MutationObserver(() => { if (userConfig.theme === 'auto') applySmartTheme(); }); themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['style', 'class', 'theme'] }); themeObserver.observe(document.body, { attributes: true, attributeFilter: ['style', 'class', 'theme'] }); const panel = el('div', { id: 'gm-settings-panel' }); const closeBtn = el('span', { className: 'gm-settings-close' }, ['×']); closeBtn.onclick = () => { panel.style.display = 'none'; }; const header = el('div', { className: 'gm-settings-header' }, [ el('span', {}, ['⚙️ 搜索枢纽设置']), closeBtn ]); const tabBasic = el('div', { className: 'gm-tab active', dataset: { target: 'tab-basic' } }, ['基础设置']); const tabEngines = el('div', { className: 'gm-tab', dataset: { target: 'tab-engines' } }, ['引擎管理']); const tabs = el('div', { className: 'gm-tabs' }, [tabBasic, tabEngines]); const cbPin = el('input', { type: 'checkbox', id: 'cb-always-pin', checked: userConfig.alwaysPin }); cbPin.onchange = (e) => { updateBasicConfig('alwaysPin', e.target.checked); }; const switchPin = el('label', { className: 'gm-switch' }, [cbPin, el('span', { className: 'gm-slider' })]); const itemPin = el('div', { className: 'gm-setting-item gm-setting-item-row' }, [ el('div', { className: 'gm-setting-label' }, [el('span', { style: { fontWeight: 'bold' } }, ['📌 始终钉在顶部 (仅搜索页)'])]), switchPin ]); const cbPreview = el('input', { type: 'checkbox', id: 'cb-show-preview', checked: userConfig.showSelectionPreview }); cbPreview.onchange = (e) => { updateBasicConfig('showSelectionPreview', e.target.checked); }; const switchPreview = el('label', { className: 'gm-switch' }, [cbPreview, el('span', { className: 'gm-slider' })]); const itemPreview = el('div', { className: 'gm-setting-item gm-setting-item-row' }, [ el('div', { className: 'gm-setting-label' }, [el('span', {}, ['💬 显示左上角选中文本提示'])]), switchPreview ]); const selectTrigger = el('select', { id: 'select-trigger' }, [ el('option', { value: 'auto', selected: userConfig.popupTrigger === 'auto' }, ['默认:选中文字后自动弹出']), el('option', { value: 'click', selected: userConfig.popupTrigger === 'click' }, ['单击:选中文字后,再次左键单击才弹出']), el('option', { value: 'none', selected: userConfig.popupTrigger === 'none' }, ['关闭:禁止选词弹出']) ]); selectTrigger.onchange = (e) => { updateBasicConfig('popupTrigger', e.target.value); }; const itemTrigger = el('div', { className: 'gm-setting-item' }, [ el('div', { className: 'gm-setting-label' }, [el('span', {}, ['🚀 划词面板弹出时机'])]), selectTrigger ]); const createSlider = (id, label, min, max, val, key) => { const range = el('input', { type: 'range', id: id, min: min, max: max, value: val }); const valSpan = el('span', { className: 'gm-setting-value', id: `val-${key}` }, [`${val}px`]); range.oninput = (e) => { valSpan.textContent = `${e.target.value}px`; updateBasicConfig(key, e.target.value); }; return el('div', { className: 'gm-setting-item' }, [ el('div', { className: 'gm-setting-label' }, [el('span', {}, [label]), valSpan]), range ]); }; const itemHeight = createSlider('range-height', '方块高度', '30', '80', userConfig.height, 'height'); const itemFont = createSlider('range-font', '字体大小', '12', '24', userConfig.fontSize, 'fontSize'); const itemGap = createSlider('range-gap', '按钮间距', '0', '40', userConfig.gap, 'gap'); const selectTheme = el('select', { id: 'select-theme' }, [ el('option', { value: 'light', selected: userConfig.theme === 'light' }, ['浅色模式 (Light)']), el('option', { value: 'dark', selected: userConfig.theme === 'dark' }, ['深色模式 (Dark)']), el('option', { value: 'auto', selected: userConfig.theme === 'auto' }, ['跟随网页背景亮度 (Auto)']) ]); selectTheme.onchange = (e) => { updateBasicConfig('theme', e.target.value); }; const itemTheme = el('div', { className: 'gm-setting-item' }, [ el('div', { className: 'gm-setting-label' }, [el('span', {}, ['智能主题配色'])]), selectTheme ]); const contentBasic = el('div', { id: 'tab-basic', className: 'gm-tab-content active' }, [itemPin, itemTrigger, itemPreview, itemHeight, itemFont, itemGap, itemTheme]); const listContainer = el('div', { id: 'gm-engine-list-container' }); const btnAddEngine = el('div', { className: 'gm-primary-btn', id: 'btn-add-engine' }, ['+ 添加自定义引擎']); btnAddEngine.onclick = () => { document.getElementById('edit-idx').value = '-1'; document.getElementById('edit-name').value = ''; document.getElementById('edit-url').value = ''; document.getElementById('edit-icon').value = ''; switchTab('tab-edit'); }; const contentEngines = el('div', { id: 'tab-engines', className: 'gm-tab-content', style: { paddingTop: '5px' } }, [listContainer, btnAddEngine]); const editIdx = el('input', { type: 'hidden', id: 'edit-idx' }); const editName = el('input', { type: 'text', id: 'edit-name', placeholder: '如: 豆瓣' }); const editUrl = el('input', { type: 'text', id: 'edit-url', placeholder: '如: https://search.douban.com/?q=%s' }); const editIcon = el('textarea', { id: 'edit-icon', placeholder: '输入图片网址,或者直接粘贴超长的 Base64 图片代码' }); const btnSaveEngine = el('div', { className: 'gm-primary-btn', style: { flex: '1' } }, ['保存']); btnSaveEngine.onclick = () => { const idx = parseInt(editIdx.value), name = editName.value.trim(), url = editUrl.value.trim(); let icon = editIcon.value.trim(); if(!name || !url.includes('%s')) return alert('请正确填写名称和包含 %s 的 URL!'); let domain = ''; try { const urlObj = new URL(url.replace('%s', 'test')); domain = urlObj.hostname.replace('www.', ''); if (!icon) icon = `https://s2.googleusercontent.com/s2/favicons?domain=${domain}&sz=64`; } catch(e) { return alert('URL 格式不合法!'); } const newEng = { id: 'custom_' + Date.now(), name, domain, url, icon, enabled: true }; if (idx === -1) userConfig.engines.push(newEng); else { newEng.id = userConfig.engines[idx].id; newEng.enabled = userConfig.engines[idx].enabled; userConfig.engines[idx] = newEng; } saveAndRender(); switchTab('tab-engines'); }; const btnCancelEngine = el('div', { className: 'gm-secondary-btn', style: { flex: '1' } }, ['取消']); btnCancelEngine.onclick = () => switchTab('tab-engines'); const actionRow = el('div', { style: { display: 'flex', gap: '10px', marginTop: '10px' } }, [btnSaveEngine, btnCancelEngine]); const contentEdit = el('div', { id: 'tab-edit', className: 'gm-tab-content' }, [ editIdx, el('div', { className: 'gm-setting-item' }, [el('div', { className: 'gm-setting-label' }, [el('span', {}, ['名称'])]), editName]), el('div', { className: 'gm-setting-item' }, [el('div', { className: 'gm-setting-label' }, [el('span', {}, ['搜索 URL (用 %s 占位)'])]), editUrl]), el('div', { className: 'gm-setting-item' }, [el('div', { className: 'gm-setting-label' }, [el('span', {}, ['图标 (支持 URL 或 Base64)'])]), editIcon]), actionRow ]); panel.appendChild(header); panel.appendChild(tabs); panel.appendChild(contentBasic); panel.appendChild(contentEngines); panel.appendChild(contentEdit); document.documentElement.appendChild(panel); const saveAndRender = () => { GM_setValue('gm_search_config', userConfig); renderTopBarEngines(); renderSettingsEngineList(); }; function renderSettingsEngineList() { listContainer.textContent = ''; userConfig.engines.forEach((eng, idx) => { let finalIcon = eng.icon ? eng.icon.trim() : ''; if (finalIcon.startsWith(' 100 && !finalIcon.startsWith('http') && !finalIcon.startsWith('data:')) { finalIcon = `data:image/svg+xml;base64,${finalIcon}`; } const img = el('img', { src: finalIcon }); img.onerror = function() { this.src = PLACEHOLDER_BASE64; }; const span = el('span', {}, [eng.name]); const info = el('div', { className: 'gm-engine-info' }, [img, span]); const btnUp = el('span', { className: 'gm-action-btn', title: '上移' }, ['▲']); btnUp.onclick = () => { if (idx > 0) { [userConfig.engines[idx-1], userConfig.engines[idx]] = [userConfig.engines[idx], userConfig.engines[idx-1]]; saveAndRender(); } }; const btnDown = el('span', { className: 'gm-action-btn', title: '下移' }, ['▼']); btnDown.onclick = () => { if (idx < userConfig.engines.length - 1) { [userConfig.engines[idx+1], userConfig.engines[idx]] = [userConfig.engines[idx], userConfig.engines[idx+1]]; saveAndRender(); } }; const btnToggle = el('span', { className: 'gm-action-btn', title: '显隐' }, [eng.enabled ? '👁️' : '🙈']); btnToggle.onclick = () => { userConfig.engines[idx].enabled = !eng.enabled; saveAndRender(); }; const btnEdit = el('span', { className: 'gm-action-btn', title: '编辑' }, ['✏️']); btnEdit.onclick = () => { document.getElementById('edit-idx').value = idx; document.getElementById('edit-name').value = eng.name; document.getElementById('edit-url').value = eng.url; document.getElementById('edit-icon').value = eng.icon; switchTab('tab-edit'); }; const btnDelete = el('span', { className: 'gm-action-btn', title: '删除', style: { color: '#ff4d4f' } }, ['✖']); btnDelete.onclick = () => { if (confirm(`确定删除 ${eng.name} 吗?`)) { userConfig.engines.splice(idx, 1); saveAndRender(); } }; const actions = el('div', { className: 'gm-engine-actions' }, [btnUp, btnDown, btnToggle, btnEdit, btnDelete]); const item = el('div', { className: `gm-engine-list-item ${eng.enabled ? '' : 'disabled'}` }, [info, actions]); listContainer.appendChild(item); }); } renderSettingsEngineList(); function switchTab(targetId) { tabBasic.classList.remove('active'); tabEngines.classList.remove('active'); contentBasic.classList.remove('active'); contentEngines.classList.remove('active'); contentEdit.classList.remove('active'); if (targetId === 'tab-basic') { tabBasic.classList.add('active'); contentBasic.classList.add('active'); } else if (targetId === 'tab-engines') { tabEngines.classList.add('active'); contentEngines.classList.add('active'); } else if (targetId === 'tab-edit') { contentEdit.classList.add('active'); } } tabBasic.onclick = () => switchTab('tab-basic'); tabEngines.onclick = () => switchTab('tab-engines'); GM_registerMenuCommand('⚙️ 搜索引擎顶栏设置', () => { panel.style.top = '50%'; panel.style.left = '50%'; panel.style.transform = 'translate(-50%, -50%)'; panel.style.display = 'flex'; }); const updateBasicConfig = (key, val) => { if (key === 'theme' || key === 'popupTrigger' || key === 'alwaysPin' || key === 'showSelectionPreview') userConfig[key] = val; else userConfig[key] = parseInt(val); GM_setValue('gm_search_config', userConfig); if (key === 'theme') applySmartTheme(); else if (key !== 'alwaysPin') { document.documentElement.style.setProperty(`--gm-config-${key.toLowerCase()}`, val + 'px'); } updateBarVisibility(); }; let isDragging = false, startX, startY, initialLeft, initialTop; header.onmousedown = (e) => { isDragging = true; const rect = panel.getBoundingClientRect(); panel.style.transform = 'none'; panel.style.left = rect.left + 'px'; panel.style.top = rect.top + 'px'; startX = e.clientX; startY = e.clientY; initialLeft = rect.left; initialTop = rect.top; document.body.style.userSelect = 'none'; }; window.addEventListener('mousemove', (e) => { if (isDragging) { panel.style.left = (initialLeft + e.clientX - startX) + 'px'; panel.style.top = (initialTop + e.clientY - startY) + 'px'; } }); window.addEventListener('mouseup', () => { if(isDragging) { isDragging = false; document.body.style.userSelect = ''; }}); }); // ===================================================================== // 7. B站 / 微博 全局劫持 (防止回车新开网页) // ===================================================================== document.addEventListener('DOMContentLoaded', () => { if (typeof isShieldPage === 'function' && isShieldPage()) return; if (currentDomain.includes('search.bilibili.com')) { const urlParams = new URLSearchParams(window.location.search); const currentKeyword = urlParams.get('keyword'); const originalOpen = window.open; window.open = function(url, name, specs) { if (typeof url === 'string' && (url.includes('search.bilibili.com') || url.startsWith('//search.bilibili.com'))) { window.location.href = url; return null; } return originalOpen.apply(this, arguments); }; const observer = new MutationObserver(() => { const containers = document.querySelectorAll('.center-search-container'); if (containers.length > 0) { containers.forEach(container => { const input = container.querySelector('input'); if (input) { if (input.hasAttribute('placeholder')) input.removeAttribute('placeholder'); if (input.placeholder) input.placeholder = ''; if (input.hasAttribute('title')) input.removeAttribute('title'); if (currentKeyword && !input.dataset.echoed) { input.value = currentKeyword; input.setAttribute('value', currentKeyword); input.dispatchEvent(new Event('input', { bubbles: true })); input.dataset.echoed = 'true'; } } const links = container.querySelectorAll('a[target="_blank"]'); links.forEach(a => { a.target = '_self'; }); }); } }); observer.observe(document.body, { childList: true, subtree: true }); window.addEventListener('keydown', (e) => { if (e.key === 'Enter') { const input = e.target.closest('.center-search-container input'); if (input) { e.preventDefault(); e.stopImmediatePropagation(); const kw = input.value.trim(); if (kw) window.location.href = `https://search.bilibili.com/all?keyword=${encodeURIComponent(kw)}`; } } }, true); window.addEventListener('click', (e) => { const isBtn = e.target.closest('.nav-search-btn') || e.target.closest('.search-btn') || e.target.closest('.center-search__search-btn') || e.target.closest('.search-button'); const container = e.target.closest('.center-search-container'); if (isBtn && container) { e.preventDefault(); e.stopImmediatePropagation(); const input = container.querySelector('input'); const kw = input ? input.value.trim() : ''; if (kw) window.location.href = `https://search.bilibili.com/all?keyword=${encodeURIComponent(kw)}`; } }, true); } if (currentDomain.includes('weibo.com') && window.location.pathname.includes('/weibo')) { const originalWeiboOpen = window.open; window.open = function(url, name, specs) { if (typeof url === 'string' && (url.includes('s.weibo.com/weibo') || url.startsWith('/weibo'))) { window.location.href = url; return null; } return originalWeiboOpen.apply(this, arguments); }; const weiboObserver = new MutationObserver(() => { const forms = document.querySelectorAll('form[target="_blank"]'); forms.forEach(form => { form.target = '_self'; }); const links = document.querySelectorAll('a[target="_blank"]'); links.forEach(a => { if (a.href && a.href.includes('s.weibo.com/weibo')) { a.target = '_self'; } }); }); weiboObserver.observe(document.body, { childList: true, subtree: true }); window.addEventListener('keydown', (e) => { if (e.key === 'Enter' && e.target.tagName === 'INPUT' && (e.target.name === 'q' || e.target.closest('[class*="search"]'))) { const kw = e.target.value.trim(); if (kw) { e.preventDefault(); e.stopImmediatePropagation(); window.location.href = `https://s.weibo.com/weibo?q=${encodeURIComponent(kw)}`; } } }, true); window.addEventListener('click', (e) => { const btn = e.target.closest('button') || e.target.closest('[class*="searchBtn"]') || e.target.closest('.s-btn-b'); const container = btn ? (btn.closest('form') || btn.closest('[class*="search"]')) : null; if (btn && container) { const input = container.querySelector('input[type="text"], input[name="q"]'); if (input && input.value.trim()) { e.preventDefault(); e.stopImmediatePropagation(); window.location.href = `https://s.weibo.com/weibo?q=${encodeURIComponent(input.value.trim())}`; } } }, true); } }); })();