搜索引擎切换器(改)

🚀 一键切换多个搜索引擎!支持Google、Bing、百度等搜索平台。可拖拽、自动隐藏,提升您的搜索效率。适配暗黑模式,让搜索更智能、更便捷!

  1. // ==UserScript==
  2. // @name 搜索引擎切换器(改)
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.2
  5. // @description 🚀 一键切换多个搜索引擎!支持Google、Bing、百度等搜索平台。可拖拽、自动隐藏,提升您的搜索效率。适配暗黑模式,让搜索更智能、更便捷!
  6. // @author coccvo
  7. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAtVJREFUWEfNlk1oE1EQx/+JK42JQVsLfpwEPURQa/1CKtKD4MWSg+hBoaCVBJR6sUqvehOMBaVaktJeQgXxIqZnhYIWilIUJCoeBcUqKobUfK6+tzvb997uJsRNGxdCNvvmzfzmP/Nm40OLL1+L44MAdBVEfz4IX88ophN9ON6zld8vw+VjAPqVM924OXh4Gfy7u7w6+gyJ+/NcAZ1l24qLqfp/ATAiUsPp3m090DuGdWvb8OV7Xtp/ZM8WzNw7wcUV95LaNgXIKH7jKcYfv+F21Iz0LT5rZP3Qzk2YTZ2UKm0DuD4xh2t/P2oQ+k2ZiF66tnfi1YevUuarNT9K5So3CwY05H+XrXVxr2MP0HFTMxZlI6Cjlx7hycuPkvP2Y+P4kSvYgIozF2197ghQrlShrfJbxpv7JvFpekDafPD8Q8xNnLJq63aCxPI52Xg6BaRULBZDKpWyZ8dHDLBr2wa8Tp92ZPxnAKtM+tIA9ZkBeXmU5xs7gvisqEj9xOeA2GT1BtLesw8w/37BCiIG3j8whBeTtywXBMJs1GFHSbi+C2qBiI6Z3eW3tlcJRiKGa2YbCoWQz+cd26DRtyHrzgpzSpmLwSkoPWO/FVhbvEYBjHKZALUyZ2u3dwdQKRYsezYQVRkaBojH40gmk1wBJwAWgGWuqmAqxobBmDSM6jWdsq5HIhFks9maALSn8Osn7h5YLyoQAfDOC8A3AB1Ugn3nhtA7nHDNQWxEUwHPJTBbwOj6WmWArmNkh7/pTcgBxEZk9xdmF7CmvdNS4k5XEOXCohqcncOQ1yak/Xoul+Pnm32Hw2FbGdRZ4XQCuIoNNqForhOAmw+aFXp/v1GydNoW0wuAVQ52UyqVoGkaMpkMotGoxEQAJoQU0yuAVRIHFaQxTxBtU1MoVvmfFb7eLIB6lTQaVy6FcZDq7WziuiPESgIsHWFBiZUGkBoXQKEVAFJVWw7wBwL5ZxuvPFSKAAAAAElFTkSuQmCC
  8. // @match *://www.google.com*/search*
  9. // @match *://chat.baidu.com/*
  10. // @match *://sou.com/*
  11. // @match *://www.n.cn/*
  12. // @match *://www.bing.com/search*
  13. // @match *://cn.bing.com/search*
  14. // @match *://www.baidu.com/s*
  15. // @match *://www.baidu.com/baidu*
  16. // @match *://chatgpt.com/*
  17. // @match *://ygocdb.com/*
  18. // @match *://weixin.sogou.com/weixin*
  19. // @match *://search.bilibili.com/all*
  20. // @match *://www.emojiall.com/*
  21. // @match *://metaso.cn/*
  22. // @match *://s.weibo.com/*
  23. // @match *://www.zhihu.com/search*
  24. // @match *://github.com/search*
  25. // @match *://www.xiaohongshu.com/explore*
  26. // @match *://www.douyin.com/search/*
  27. // @grant unsafeWindow
  28. // @grant window.onload
  29. // @run-at document-body
  30. // @license MIT
  31. // ==/UserScript==
  32.  
  33. (function() {
  34. 'use strict';
  35.  
  36. const urlMapping = [
  37. { name: "谷歌🎨", searchUrl: "https://www.google.com/search?q=", keyName: "q", testUrl: /https:\/\/www\.google\.(com|com\.hk)\/search.*/ },
  38. { name: "百度AI💡", searchUrl: "https://chat.baidu.com/search?word=", keyName: "word", testUrl: /https:\/\/chat.baidu.com\/search\.*/ },
  39. { name: "秘塔AI🤖", searchUrl: "https://metaso.cn/?q=", keyName: "q", testUrl: /https:\/\/metaso\.cn\/*/ },
  40. { name: "纳米AI⚛️", searchUrl: "https://www.n.cn/?q=", keyName: "q", testUrl: /https:\/\/www\.n\.cn\/*/ },
  41. { name: "Bilibili📺", searchUrl: "https://search.bilibili.com/all?keyword=", keyName: "keyword", testUrl: /https:\/\/search\.bilibili\.com\/all.*/ },
  42. { name: "Bing🔍️", searchUrl: "https://cn.bing.com/search?q=", keyName: "q", testUrl: /https:\/\/cn\.bing\.com\/search.*/ },
  43. { name: "百度🐻", searchUrl: "https://www.baidu.com/s?wd=", keyName: "wd", testUrl: /https:\/\/www\.baidu\.com\/(s|baidu).*/ },
  44. { name: "百鸽🟦", searchUrl: "https://ygocdb.com/?search=", keyName: "search", testUrl: /https:\/\/ygocdb\.com\/.*/ },
  45. { name: "微信💚", searchUrl: "https://weixin.sogou.com/weixin?type=2&s_from=input&query=", keyName: "query", testUrl: /https:\/\/weixin\.sogou\.com\/weixin.*/ },
  46. { name: "微博👁️", searchUrl: "https://s.weibo.com/weibo?q=", keyName: "q", testUrl: /https:\/\/s\.weibo\.com\/.*/ },
  47. { name: "知乎📚", searchUrl: "https://www.zhihu.com/search?q=", keyName: "q", testUrl: /https:\/\/www\.zhihu\.com\/search.*/ },
  48. { name: "Emojiall😀", searchUrl: "https://www.emojiall.com/zh-hans/search_results?keywords=", keyName: "keywords", testUrl: /https:\/\/www\.emojiall\.com\/zh-hans\/search_results.*/ },
  49. { name: "GitHub🐙", searchUrl: "https://github.com/search?q=", keyName: "q", testUrl: /https:\/\/github\.com\/search.*/ },
  50. { name: "小红书📕", searchUrl: "https://www.xiaohongshu.com/explore?q=", keyName: "q", testUrl: /https:\/\/www\.xiaohongshu\.com\/explore.*/ },
  51. { name: "抖音🎵", searchUrl: "https://www.douyin.com/search/", keyName: "q", testUrl: /https:\/\/www\.douyin\.com\/search\/.*/ },
  52. ];
  53.  
  54. const ICON_SIZE = '28px';
  55. const LIST_WIDTH = '100px';
  56. const FONT_SIZE = '14px';
  57. const AUTO_HIDE_DELAY = 5000; // 5 seconds
  58.  
  59. function getQueryVariable(variable) {
  60. const query = window.location.search.substring(1);
  61. const vars = query.split('&');
  62. for (let i = 0; i < vars.length; i++) {
  63. const pair = vars[i].split('=');
  64. if (decodeURIComponent(pair[0]) === variable) {
  65. return decodeURIComponent(pair[1]);
  66. }
  67. }
  68. if (variable === "q" && window.location.pathname.startsWith("/search/")) {
  69. return decodeURIComponent(window.location.pathname.replace("/search/", ""));
  70. }
  71. return "";
  72. }
  73.  
  74. function getKeywords() {
  75. for (const item of urlMapping) {
  76. if (item.testUrl.test(window.location.href)) {
  77. return getQueryVariable(item.keyName);
  78. }
  79. }
  80. return "";
  81. }
  82.  
  83. function isDarkMode() {
  84. return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
  85. }
  86.  
  87. function createStyle() {
  88. const style = document.createElement('style');
  89. style.textContent = `
  90. #search-app-box {
  91. position: fixed;
  92. top: 100px;
  93. left: 0;
  94. width: ${ICON_SIZE};
  95. height: ${ICON_SIZE};
  96. background-color: transparent;
  97. z-index: 2147483647;
  98. cursor: move;
  99. font-size: ${FONT_SIZE};
  100. transition: left 0.3s ease-in-out;
  101. }
  102. #search-app-icon {
  103. width: 100%;
  104. height: 100%;
  105. display: flex;
  106. justify-content: center;
  107. align-items: center;
  108. font-size: ${ICON_SIZE};
  109. user-select: none;
  110. background-color: rgba(255, 255, 255, 0.7);
  111. border-radius: 0 50% 50% 0;
  112. }
  113. #search-engine-list {
  114. position: absolute;
  115. top: 0;
  116. left: ${ICON_SIZE};
  117. width: ${LIST_WIDTH};
  118. max-height: 70vh;
  119. overflow-y: auto;
  120. background-color: rgba(255, 255, 255, 0.9);
  121. border-radius: 8px;
  122. box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  123. opacity: 0;
  124. visibility: hidden;
  125. transform: translateX(-10px);
  126. transition: opacity 0.3s, visibility 0.3s, transform 0.3s;
  127. }
  128. #search-engine-list a {
  129. display: block;
  130. padding: 8px 12px;
  131. color: #333;
  132. text-decoration: none;
  133. transition: background-color 0.3s;
  134. }
  135. #search-engine-list a:hover {
  136. background-color: rgba(0, 0, 0, 0.1);
  137. }
  138. .dark-mode #search-app-icon {
  139. background-color: rgba(50, 50, 50, 0.7);
  140. }
  141. .dark-mode #search-engine-list {
  142. background-color: rgba(50, 50, 50, 0.9);
  143. }
  144. .dark-mode #search-engine-list a {
  145. color: #fff;
  146. }
  147. .dark-mode #search-engine-list a:hover {
  148. background-color: rgba(255, 255, 255, 0.1);
  149. }
  150. #search-app-box.hidden {
  151. left: -8px;
  152. }
  153. #search-app-box.dragging {
  154. transition: none;
  155. }
  156. `;
  157. document.head.appendChild(style);
  158. }
  159.  
  160. function createSearchBox() {
  161. const div = document.createElement('div');
  162. div.id = 'search-app-box';
  163.  
  164. const icon = document.createElement('div');
  165. icon.id = 'search-app-icon';
  166. icon.innerText = '🔍';
  167. div.appendChild(icon);
  168.  
  169. const listContainer = document.createElement('div');
  170. listContainer.id = 'search-engine-list';
  171.  
  172. for (const item of urlMapping) {
  173. const a = document.createElement('a');
  174. a.href = item.searchUrl + encodeURIComponent(getKeywords());
  175. a.innerText = item.name;
  176. a.addEventListener('click', (e) => {
  177. e.preventDefault();
  178. window.location.href = a.href;
  179. });
  180. listContainer.appendChild(a);
  181. }
  182.  
  183. div.appendChild(listContainer);
  184. document.body.appendChild(div);
  185.  
  186. let hideTimeout;
  187.  
  188. function showSearchBox() {
  189. div.classList.remove('hidden');
  190. clearTimeout(hideTimeout);
  191. }
  192.  
  193. function hideSearchBox() {
  194. div.classList.add('hidden');
  195. }
  196.  
  197. function resetHideTimer() {
  198. clearTimeout(hideTimeout);
  199. hideTimeout = setTimeout(hideSearchBox, AUTO_HIDE_DELAY);
  200. }
  201.  
  202. div.addEventListener('mouseenter', () => {
  203. showSearchBox();
  204. listContainer.style.opacity = '1';
  205. listContainer.style.visibility = 'visible';
  206. listContainer.style.transform = 'translateX(0)';
  207. });
  208.  
  209. div.addEventListener('mouseleave', () => {
  210. listContainer.style.opacity = '0';
  211. listContainer.style.visibility = 'hidden';
  212. listContainer.style.transform = 'translateX(-10px)';
  213. resetHideTimer();
  214. });
  215.  
  216. // 拖拽功能
  217. let isDragging = false;
  218. let startX, startY, startLeft, startTop;
  219.  
  220. div.addEventListener('mousedown', (e) => {
  221. isDragging = true;
  222. startX = e.clientX;
  223. startY = e.clientY;
  224. startLeft = div.offsetLeft;
  225. startTop = div.offsetTop;
  226. showSearchBox();
  227. e.preventDefault();
  228. div.classList.add('dragging');
  229. });
  230.  
  231. document.addEventListener('mousemove', (e) => {
  232. if (!isDragging) return;
  233. const dx = e.clientX - startX;
  234. const dy = e.clientY - startY;
  235. div.style.left = `${startLeft + dx}px`;
  236. div.style.top = `${startTop + dy}px`;
  237. });
  238.  
  239. document.addEventListener('mouseup', () => {
  240. isDragging = false;
  241. resetHideTimer();
  242. div.classList.remove('dragging');
  243. });
  244.  
  245. // 初始化隐藏定时器
  246. resetHideTimer();
  247. }
  248.  
  249. function updateTheme() {
  250. document.body.classList.toggle('dark-mode', isDarkMode());
  251. }
  252.  
  253. function init() {
  254. createStyle();
  255. createSearchBox();
  256. updateTheme();
  257.  
  258. // 监听主题变化
  259. window.matchMedia('(prefers-color-scheme: dark)').addListener(updateTheme);
  260. }
  261.  
  262. // 使用 MutationObserver 来确保脚本在动态加载的页面上也能正常工作
  263. const observer = new MutationObserver((mutations, obs) => {
  264. const body = document.querySelector('body');
  265. if (body) {
  266. init();
  267. obs.disconnect();
  268. }
  269. });
  270.  
  271. observer.observe(document.documentElement, {
  272. childList: true,
  273. subtree: true
  274. });
  275. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址