// ==UserScript== // @name 移动端搜索引擎快捷跳转(仿via // @namespace https://tampermonkey.net/ // @version 1.7 // @description // @author 小骨 // @match *://*.google.com/* // @match *://*.google.com.hk/* // @match *://*.baidu.com/* // @match *://*.bing.com/* // @match *://*.yandex.com/* // @match *://*.yandex.ru/* // @match *://*.sogou.com/* // @match *://*.so.com/* // @match *://search.brave.com/* // @run-at document-end // @grant none // ==/UserScript== (function () { 'use strict'; const STORAGE_KEY = 'mobile_engine_switcher_config_v3'; // 1. 默认搜索引擎列表配置 const DEFAULT_ENGINES = [ { name: '百度', host: 'baidu.com', url: 'https://m.baidu.com/s?word={q}' }, { name: '谷歌', host: 'google.com', url: 'https://www.google.com/search?q={q}' }, { name: '必应', host: 'bing.com', url: 'https://cn.bing.com/search?q={q}' }, { name: 'Yandex图源', host: 'yandex.com/images', url: 'https://yandex.com/images/search?text={q}' }, { name: 'Yandex', host: 'yandex.com', url: 'https://yandex.com/search/?text={q}' }, { name: '搜狗', host: 'sogou.com', url: 'https://m.sogou.com/web/searchList.jsp?keyword={q}' }, { name: 'Brave', host: 'brave.com', url: 'https://search.brave.com/search?q={q}' } ]; // 读取本地存储的引擎列表 function getSavedEngines() { try { const saved = localStorage.getItem(STORAGE_KEY); if (saved) { const parsed = JSON.parse(saved); if (Array.isArray(parsed) && parsed.length > 0) { return parsed; } } } catch (e) { console.error('读取设置失败', e); } return DEFAULT_ENGINES; } // 保存引擎列表 function saveEngines(engines) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(engines)); } catch (e) { console.error('保存设置失败', e); } } // 构建带关键词的链接 function buildEngineUrl(urlTemplate, query) { if (urlTemplate.includes('{q}')) { return urlTemplate.replace(/\{q\}/g, encodeURIComponent(query)); } const separator = urlTemplate.includes('?') ? '&' : '?'; return `${urlTemplate}${separator}q=${encodeURIComponent(query)}`; } // 提取当前页面的搜索关键词(含 Yandex 的 text 参数) function getSearchQuery() { let q = ''; // 1. 优先从 URL Query 参数提取 const searchParams = new URLSearchParams(window.location.search); q = searchParams.get('q') || searchParams.get('word') || searchParams.get('wd') || searchParams.get('keyword') || searchParams.get('query') || searchParams.get('text'); // 2. 支持 Hash 路由参数 if (!q && window.location.hash.includes('?')) { const hashQuery = window.location.hash.substring(window.location.hash.indexOf('?')); const hashParams = new URLSearchParams(hashQuery); q = hashParams.get('q') || hashParams.get('word') || hashParams.get('wd') || hashParams.get('keyword') || hashParams.get('query') || hashParams.get('text'); } // 3. 若 URL 中无参数,尝试从搜索框获取 if (!q) { const input = document.querySelector('input[type="search"], input[name="q"], input[name="wd"], input[name="word"], input[name="keyword"], input[name="text"]'); if (input && input.value) { q = input.value; } } return q ? q.trim() : ''; } // 判断是否为当前搜索引擎 function isCurrentEngine(host) { if (!host) return false; const currentUrl = window.location.href.toLowerCase(); const targetHost = host.toLowerCase(); // 包含路径的 host 匹配(如 yandex.com/images) if (targetHost.includes('/')) { return currentUrl.includes(targetHost); } // 特殊处理:访问 Yandex 图搜页时,主站 Yandex 不进行高亮显示 if (targetHost.includes('yandex') && !targetHost.includes('/images') && currentUrl.includes('/images')) { return false; } return window.location.hostname.includes(targetHost); } // 清理底栏及 CSS function removeSwitcher() { const bar = document.getElementById('mobile-engine-switcher-bar'); if (bar) bar.remove(); const style = document.getElementById('mobile-engine-switcher-style'); if (style) style.remove(); } // HTML 字符转义,防止 XSS function escapeHtml(str) { return (str || '').replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); } // 创建或更新底部跳转工具栏 function renderSwitcher() { const query = getSearchQuery(); if (!query) { removeSwitcher(); return; } const engines = getSavedEngines(); // 注入样式 if (!document.getElementById('mobile-engine-switcher-style')) { const style = document.createElement('style'); style.id = 'mobile-engine-switcher-style'; style.textContent = ` #mobile-engine-switcher-bar { position: fixed !important; bottom: 0 !important; left: 0 !important; right: 0 !important; width: 100% !important; z-index: 2147483647 !important; display: flex !important; align-items: center !important; gap: 8px !important; padding: 6px 10px calc(6px + env(safe-area-inset-bottom, 0px)) 10px !important; background-color: #000000 !important; border-top: 1px solid rgba(255, 255, 255, 0.05) !important; overflow-x: auto !important; white-space: nowrap !important; -webkit-overflow-scrolling: touch !important; box-sizing: border-box !important; } #mobile-engine-switcher-bar::-webkit-scrollbar { display: none !important; } .engine-pill-btn { flex: 0 0 auto !important; height: 27px !important; padding: 0 14px !important; border-radius: 9999px !important; font-size: 13px !important; font-weight: 400 !important; line-height: 1 !important; text-decoration: none !important; color: #e3e3e3 !important; background-color: #000000 !important; border: 1px solid #303030 !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; box-sizing: border-box !important; user-select: none !important; -webkit-tap-highlight-color: transparent !important; font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif !important; } .engine-pill-btn.active { border-color: #8ab4f8 !important; color: #8ab4f8 !important; background-color: rgba(138, 180, 248, 0.12) !important; font-weight: 400 !important; } .engine-pill-btn.settings-btn { padding: 0 9px !important; color: #888888 !important; border-color: #2a2a2a !important; } .engine-pill-btn.settings-btn:active { color: #ffffff !important; border-color: #505050 !important; background-color: #111111 !important; } body { padding-bottom: 42px !important; } /* 弹窗样式 */ #mobile-engine-modal-overlay { position: fixed !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; width: 100vw !important; height: 100vh !important; background: rgba(0, 0, 0, 0.8) !important; backdrop-filter: blur(4px) !important; -webkit-backdrop-filter: blur(4px) !important; z-index: 2147483648 !important; display: flex !important; align-items: center !important; justify-content: center !important; padding: 16px !important; box-sizing: border-box !important; } .engine-modal-card { background: #141416 !important; border: 1px solid #2d2d32 !important; border-radius: 14px !important; width: 100% !important; max-width: 360px !important; max-height: 80vh !important; display: flex !important; flex-direction: column !important; box-shadow: 0 16px 36px rgba(0,0,0,0.8) !important; color: #e3e3e3 !important; font-family: -apple-system, BlinkMacSystemFont, sans-serif !important; overflow: hidden !important; } .engine-modal-header { display: flex !important; justify-content: space-between !important; align-items: center !important; padding: 12px 16px !important; border-bottom: 1px solid #26262a !important; font-size: 14px !important; font-weight: 500 !important; color: #ffffff !important; } .engine-modal-close { background: transparent !important; border: none !important; color: #888888 !important; font-size: 20px !important; cursor: pointer !important; padding: 0 4px !important; line-height: 1 !important; } .engine-modal-body { padding: 14px 16px !important; overflow-y: auto !important; flex: 1 !important; } .engine-modal-section-title { font-size: 11px !important; color: #777777 !important; margin-bottom: 8px !important; letter-spacing: 0.5px !important; } .engine-list-item { display: flex !important; align-items: center !important; justify-content: space-between !important; padding: 8px 10px !important; background: #1c1c20 !important; border: 1px solid #28282e !important; border-radius: 8px !important; margin-bottom: 6px !important; } .engine-item-info { display: flex !important; flex-direction: column !important; gap: 2px !important; overflow: hidden !important; margin-right: 8px !important; } .engine-item-name { font-size: 13px !important; color: #ffffff !important; } .engine-item-host { font-size: 11px !important; color: #666666 !important; white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; } .engine-item-actions { display: flex !important; align-items: center !important; gap: 4px !important; flex-shrink: 0 !important; } .engine-action-btn { background: #26262c !important; border: 1px solid #36363e !important; color: #cccccc !important; border-radius: 4px !important; padding: 2px 7px !important; font-size: 11px !important; cursor: pointer !important; line-height: 1.4 !important; } .engine-action-btn.delete-btn { color: #ff6b6b !important; border-color: rgba(255,107,107,0.2) !important; background: rgba(255,107,107,0.08) !important; } .engine-add-form { background: #18181c !important; border: 1px solid #26262c !important; border-radius: 10px !important; padding: 12px !important; margin-top: 12px !important; } .engine-input-group { margin-bottom: 8px !important; } .engine-input-group label { display: block !important; font-size: 11px !important; color: #777777 !important; margin-bottom: 4px !important; } .engine-input { width: 100% !important; box-sizing: border-box !important; background: #101012 !important; border: 1px solid #2a2a30 !important; border-radius: 6px !important; color: #ffffff !important; padding: 6px 10px !important; font-size: 12px !important; outline: none !important; } .engine-input:focus { border-color: #8ab4f8 !important; } .engine-add-btn { width: 100% !important; background: #8ab4f8 !important; color: #000000 !important; border: none !important; border-radius: 6px !important; padding: 7px !important; font-size: 12px !important; font-weight: 500 !important; cursor: pointer !important; margin-top: 4px !important; } .engine-modal-footer { padding: 10px 16px !important; border-top: 1px solid #26262a !important; display: flex !important; justify-content: space-between !important; align-items: center !important; background: #141416 !important; } .engine-reset-btn { background: transparent !important; border: none !important; color: #777777 !important; font-size: 12px !important; cursor: pointer !important; text-decoration: underline !important; } .engine-confirm-btn { background: #2a2a30 !important; border: 1px solid #3a3a42 !important; color: #ffffff !important; border-radius: 6px !important; padding: 5px 14px !important; font-size: 12px !important; cursor: pointer !important; } `; document.head.appendChild(style); } // 创建或清空容器 let bar = document.getElementById('mobile-engine-switcher-bar'); if (!bar) { bar = document.createElement('div'); bar.id = 'mobile-engine-switcher-bar'; document.body.appendChild(bar); } else { bar.innerHTML = ''; } // 渲染引擎跳转按钮 engines.forEach(engine => { const btn = document.createElement('a'); btn.className = 'engine-pill-btn' + (isCurrentEngine(engine.host) ? ' active' : ''); btn.textContent = engine.name; btn.href = 'javascript:void(0);'; btn.addEventListener('click', (e) => { e.preventDefault(); if (isCurrentEngine(engine.host)) return; const currentQuery = getSearchQuery(); if (currentQuery) { window.location.href = buildEngineUrl(engine.url, currentQuery); } }); bar.appendChild(btn); }); // 拼接最右侧极简黑白设置按钮(SVG 图标) const settingsBtn = document.createElement('a'); settingsBtn.className = 'engine-pill-btn settings-btn'; settingsBtn.title = '设置搜索引擎'; settingsBtn.href = 'javascript:void(0);'; settingsBtn.innerHTML = ` `; settingsBtn.addEventListener('click', (e) => { e.preventDefault(); openSettingsModal(); }); bar.appendChild(settingsBtn); } // 打开设置弹窗 function openSettingsModal() { if (document.getElementById('mobile-engine-modal-overlay')) return; const overlay = document.createElement('div'); overlay.id = 'mobile-engine-modal-overlay'; overlay.innerHTML = `