- // ==UserScript==
- // @name 搜索引擎切换器 / Search Engine Switcher
- // @namespace http://tampermonkey.net/
- // @version 0.2.1
- // @description 🚀 一键切换多个搜索引擎!支持Google、Bing、百度、ChatGPT、Perplexity等13大搜索平台。可拖拽、自动隐藏,提升您的搜索效率。适配暗黑模式,让搜索更智能、更便捷!
- // @author WUJI (微信: wujiai666)
- // @match *://www.google.com*/search*
- // @match *://www.bing.com/search*
- // @match *://cn.bing.com/search*
- // @match *://www.baidu.com/s*
- // @match *://www.baidu.com/baidu*
- // @match *://chatgpt.com/*
- // @match *://metaso.cn/*
- // @match *://weixin.sogou.com/weixin*
- // @match *://search.bilibili.com/all*
- // @match *://www.youtube.com/results*
- // @match *://m.youtube.com/results*
- // @match *://www.zhihu.com/search*
- // @match *://github.com/search*
- // @match *://www.xiaohongshu.com/explore*
- // @match *://www.douyin.com/search/*
- // @match *://www.perplexity.ai/*
- // @grant unsafeWindow
- // @grant window.onload
- // @run-at document-body
- // @license MIT
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- const urlMapping = [
- { name: "Google", searchUrl: "https://www.google.com/search?q=", keyName: "q", testUrl: /https:\/\/www\.google\.(com|com\.hk)\/search.*/ },
- { name: "Bing", searchUrl: "https://www.bing.com/search?q=", keyName: "q", testUrl: /https:\/\/(www|cn)\.bing\.com\/search.*/ },
- { name: "百度", searchUrl: "https://www.baidu.com/s?wd=", keyName: "wd", testUrl: /https:\/\/www\.baidu\.com\/(s|baidu).*/ },
- { name: "ChatGPT", searchUrl: "https://chatgpt.com/?hints=search&q=", keyName: "q", testUrl: /https:\/\/chatgpt\.com\/.*/ },
- { name: "秘塔", searchUrl: "https://metaso.cn/?q=", keyName: "q", testUrl: /https:\/\/metaso\.cn\/.*/ },
- { name: "微信", searchUrl: "https://weixin.sogou.com/weixin?type=2&s_from=input&query=", keyName: "query", testUrl: /https:\/\/weixin\.sogou\.com\/weixin.*/ },
- { name: "哔站", searchUrl: "https://search.bilibili.com/all?keyword=", keyName: "keyword", testUrl: /https:\/\/search\.bilibili\.com\/all.*/ },
- { name: "油管", searchUrl: "https://www.youtube.com/results?search_query=", keyName: "search_query", testUrl: /https:\/\/(www|m)\.youtube\.com\/results.*/ },
- { name: "知乎", searchUrl: "https://www.zhihu.com/search?q=", keyName: "q", testUrl: /https:\/\/www\.zhihu\.com\/search.*/ },
- { name: "GitHub", searchUrl: "https://github.com/search?q=", keyName: "q", testUrl: /https:\/\/github\.com\/search.*/ },
- { name: "小红书", searchUrl: "https://www.xiaohongshu.com/explore?q=", keyName: "q", testUrl: /https:\/\/www\.xiaohongshu\.com\/explore.*/ },
- { name: "抖音", searchUrl: "https://www.douyin.com/search/", keyName: "q", testUrl: /https:\/\/www\.douyin\.com\/search\/.*/ },
- { name: "Perplexity", searchUrl: "https://www.perplexity.ai/?q=", keyName: "q", testUrl: /https:\/\/www\.perplexity\.ai\/.*/ },
- ];
-
- const ICON_SIZE = '32px';
- const LIST_WIDTH = '100px';
- const FONT_SIZE = '14px';
- const AUTO_HIDE_DELAY = 5000; // 5 seconds
-
- function getQueryVariable(variable) {
- const query = window.location.search.substring(1);
- const vars = query.split('&');
- for (let i = 0; i < vars.length; i++) {
- const pair = vars[i].split('=');
- if (decodeURIComponent(pair[0]) === variable) {
- return decodeURIComponent(pair[1]);
- }
- }
- if (variable === "q" && window.location.pathname.startsWith("/search/")) {
- return decodeURIComponent(window.location.pathname.replace("/search/", ""));
- }
- return "";
- }
-
- function getKeywords() {
- for (const item of urlMapping) {
- if (item.testUrl.test(window.location.href)) {
- return getQueryVariable(item.keyName);
- }
- }
- return "";
- }
-
- function isDarkMode() {
- return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
- }
-
- function createStyle() {
- const style = document.createElement('style');
- style.textContent = `
- #search-app-box {
- position: fixed;
- top: 100px;
- left: 0;
- width: ${ICON_SIZE};
- height: ${ICON_SIZE};
- background-color: transparent;
- z-index: 2147483647;
- cursor: move;
- font-size: ${FONT_SIZE};
- transition: left 0.3s ease-in-out;
- }
- #search-app-icon {
- width: 100%;
- height: 100%;
- display: flex;
- justify-content: center;
- align-items: center;
- font-size: ${ICON_SIZE};
- user-select: none;
- background-color: rgba(255, 255, 255, 0.7);
- border-radius: 0 50% 50% 0;
- }
- #search-engine-list {
- position: absolute;
- top: 0;
- left: ${ICON_SIZE};
- width: ${LIST_WIDTH};
- max-height: 70vh;
- overflow-y: auto;
- background-color: rgba(255, 255, 255, 0.9);
- border-radius: 8px;
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
- opacity: 0;
- visibility: hidden;
- transform: translateX(-10px);
- transition: opacity 0.3s, visibility 0.3s, transform 0.3s;
- }
- #search-engine-list a {
- display: block;
- padding: 8px 12px;
- color: #333;
- text-decoration: none;
- transition: background-color 0.3s;
- }
- #search-engine-list a:hover {
- background-color: rgba(0, 0, 0, 0.1);
- }
- .dark-mode #search-app-icon {
- background-color: rgba(50, 50, 50, 0.7);
- }
- .dark-mode #search-engine-list {
- background-color: rgba(50, 50, 50, 0.9);
- }
- .dark-mode #search-engine-list a {
- color: #fff;
- }
- .dark-mode #search-engine-list a:hover {
- background-color: rgba(255, 255, 255, 0.1);
- }
- #search-app-box.hidden {
- left: -8px;
- }
- #search-app-box.dragging {
- transition: none;
- }
- `;
- document.head.appendChild(style);
- }
-
- function createSearchBox() {
- const div = document.createElement('div');
- div.id = 'search-app-box';
-
- const icon = document.createElement('div');
- icon.id = 'search-app-icon';
- icon.innerText = '🔍';
- div.appendChild(icon);
-
- const listContainer = document.createElement('div');
- listContainer.id = 'search-engine-list';
-
- for (const item of urlMapping) {
- const a = document.createElement('a');
- a.href = item.searchUrl + encodeURIComponent(getKeywords());
- a.innerText = item.name;
- a.addEventListener('click', (e) => {
- e.preventDefault();
- window.location.href = a.href;
- });
- listContainer.appendChild(a);
- }
-
- div.appendChild(listContainer);
- document.body.appendChild(div);
-
- let hideTimeout;
-
- function showSearchBox() {
- div.classList.remove('hidden');
- clearTimeout(hideTimeout);
- }
-
- function hideSearchBox() {
- div.classList.add('hidden');
- }
-
- function resetHideTimer() {
- clearTimeout(hideTimeout);
- hideTimeout = setTimeout(hideSearchBox, AUTO_HIDE_DELAY);
- }
-
- div.addEventListener('mouseenter', () => {
- showSearchBox();
- listContainer.style.opacity = '1';
- listContainer.style.visibility = 'visible';
- listContainer.style.transform = 'translateX(0)';
- });
-
- div.addEventListener('mouseleave', () => {
- listContainer.style.opacity = '0';
- listContainer.style.visibility = 'hidden';
- listContainer.style.transform = 'translateX(-10px)';
- resetHideTimer();
- });
-
- // 拖拽功能
- let isDragging = false;
- let startX, startY, startLeft, startTop;
-
- div.addEventListener('mousedown', (e) => {
- isDragging = true;
- startX = e.clientX;
- startY = e.clientY;
- startLeft = div.offsetLeft;
- startTop = div.offsetTop;
- showSearchBox();
- e.preventDefault();
- div.classList.add('dragging');
- });
-
- document.addEventListener('mousemove', (e) => {
- if (!isDragging) return;
- const dx = e.clientX - startX;
- const dy = e.clientY - startY;
- div.style.left = `${startLeft + dx}px`;
- div.style.top = `${startTop + dy}px`;
- });
-
- document.addEventListener('mouseup', () => {
- isDragging = false;
- resetHideTimer();
- div.classList.remove('dragging');
- });
-
- // 初始化隐藏定时器
- resetHideTimer();
- }
-
- function updateTheme() {
- document.body.classList.toggle('dark-mode', isDarkMode());
- }
-
- function init() {
- createStyle();
- createSearchBox();
- updateTheme();
-
- // 监听主题变化
- window.matchMedia('(prefers-color-scheme: dark)').addListener(updateTheme);
- }
-
- // 使用 MutationObserver 来确保脚本在动态加载的页面上也能正常工作
- const observer = new MutationObserver((mutations, obs) => {
- const body = document.querySelector('body');
- if (body) {
- init();
- obs.disconnect();
- }
- });
-
- observer.observe(document.documentElement, {
- childList: true,
- subtree: true
- });
- })();