// ==UserScript==
// @name 全能搜索引擎切换助手
// @namespace http://tampermonkey.net/
// @version 1.6
// @description 在搜索结果页面添加智能搜索引擎切换功能,支持多种布局和自定义设置
// @author Yuze
// @copyright 2025, Yuze (https://gf.qytechs.cn/users/Yuze Guitar)
// @license MIT
// @match *://www.google.com/search*
// @match *://www.google.co*/search*
// @match *://www.bing.com/search*
// @match *://cn.bing.com/search*
// @match *://www.baidu.com/s*
// @match *://www.sogou.com/web*
// @match *://www.so.com/s*
// @match *://duckduckgo.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
// 记录调试信息
const DEBUG = true;
function log(...args) {
if (DEBUG) {
console.log('[搜索引擎切换助手]', ...args);
}
}
// 错误处理
function handleError(error, context) {
console.error(`[搜索引擎切换助手] 错误 (${context}):`, error);
if (DEBUG) {
console.trace();
}
}
// 检测操作系统
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
const modifierKey = isMac ? '⌘' : 'Alt+';
const modifierKeyCode = isMac ? 'metaKey' : 'altKey';
// 定义搜索引擎
const defaultEngines = [
{
name: 'Google',
icon: 'https://www.google.com/favicon.ico',
url: 'https://www.google.com/search?q={query}',
matchPattern: 'google\\.[^/]+/search',
queryParam: 'q',
shortcut: 'G'
},
{
name: 'Bing',
icon: 'https://www.bing.com/favicon.ico',
url: 'https://www.bing.com/search?q={query}',
matchPattern: 'bing\\.com/search',
queryParam: 'q',
shortcut: 'B'
},
{
name: '百度',
icon: 'https://www.baidu.com/favicon.ico',
url: 'https://www.baidu.com/s?wd={query}',
matchPattern: 'baidu\\.com/s',
queryParam: 'wd',
shortcut: 'D'
},
{
name: 'DuckDuckGo',
// 内置图标数据,确保在任何环境下都能显示
icon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0iI0RFNTgzMyIgZD0iTTEyIDFDNS45MiAxIDEgNS45MiAxIDEyczQuOTIgMTEgMTEgMTEgMTEtNC45MiAxMS0xMVMxOC4wOCAxIDEyIDF6bTMuNTggMTQuMWMwIC4wOS0uMDUuMi0uMTMuMjdsLS45My43Ni0uMDIuMDJhLjM4LjM4IDAgMCAxLS4yOC4xMmgtNS4xM2MtLjIyIDAtLjQtLjE4LS40LS40di0uOTctLjAxYzAtLjIyLjE4LS40LjQtLjRoNS4xNWEuMzguMzggMCAwIDEgLjI2LjEybC4wMS4wMi45My43NmEuMzQuMzQgMCAwIDEgLjEzLjI3eiIvPjwvc3ZnPg==',
url: 'https://duckduckgo.com/?q={query}',
matchPattern: 'duckduckgo\\.com',
queryParam: 'q',
shortcut: 'K'
}
];
// 添加CSS样式
function addStyles() {
try {
GM_addStyle(`
/* 主容器 */
#search-engine-switcher {
position: fixed;
top: 120px;
left: 10px;
z-index: 9999;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
padding: 10px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 14px;
display: flex;
flex-direction: column;
gap: 12px;
transition: all 0.3s ease;
min-width: 120px;
max-width: 180px;
}
/* 深色模式 */
@media (prefers-color-scheme: dark) {
#search-engine-switcher {
background: #333;
color: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}
#search-engine-switcher .engine-btn {
color: #eee;
}
#search-engine-switcher .engine-btn:hover {
background: #444;
}
#search-engine-switcher .collapse-btn {
color: #ccc;
}
#search-engine-switcher.collapsed {
background: rgba(51, 51, 51, 0.9);
}
}
/* 折叠状态 - 优化折叠后的外观 */
#search-engine-switcher.collapsed {
padding: 8px 10px;
min-width: unset;
max-width: unset;
width: auto;
}
#search-engine-switcher.collapsed .engine-container {
display: none;
}
#search-engine-switcher.collapsed .switcher-header {
margin: 0;
padding: 0;
}
#search-engine-switcher.collapsed .collapse-btn {
transform: rotate(180deg);
margin-left: 5px;
}
#search-engine-switcher.collapsed .switcher-title {
margin: 0;
font-size: 12px;
white-space: nowrap;
}
/* 头部样式 */
.switcher-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
}
.switcher-title {
font-weight: bold;
font-size: 14px;
margin: 0;
user-select: none;
}
.settings-btn, .collapse-btn {
background: none;
border: none;
cursor: pointer;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
opacity: 0.7;
transition: opacity 0.2s;
}
.settings-btn:hover, .collapse-btn:hover {
opacity: 1;
}
.header-actions {
display: flex;
gap: 8px;
}
/* 搜索引擎按钮容器 */
.engine-container {
display: flex;
flex-direction: column;
gap: 8px;
}
/* 搜索引擎按钮 */
.engine-btn {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 8px;
border-radius: 6px;
border: none;
background: none;
cursor: pointer;
color: #333;
font-size: 14px;
text-align: left;
transition: background 0.2s;
}
.engine-btn:hover {
background: #f0f0f0;
}
.engine-icon {
width: 16px;
height: 16px;
object-fit: contain;
}
.shortcut {
margin-left: auto;
padding: 2px 4px;
border-radius: 3px;
background: #f0f0f0;
color: #666;
font-size: 10px;
}
@media (prefers-color-scheme: dark) {
.shortcut {
background: #444;
color: #ccc;
}
}
/* 设置面板样式 */
.settings-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
}
.settings-container {
background: #fff;
border-radius: 8px;
padding: 20px;
width: 90%;
max-width: 400px;
max-height: 80vh;
overflow-y: auto;
}
@media (prefers-color-scheme: dark) {
.settings-container {
background: #333;
color: #fff;
}
}
.settings-header {
font-weight: bold;
font-size: 18px;
margin-bottom: 15px;
}
.settings-section {
margin-bottom: 20px;
}
.section-title {
font-weight: bold;
margin-bottom: 10px;
}
.checkbox-group {
display: flex;
flex-direction: column;
gap: 10px;
}
.settings-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 20px;
}
.settings-button {
padding: 8px 16px;
border-radius: 4px;
border: none;
cursor: pointer;
}
.cancel-button {
background: #f0f0f0;
color: #333;
}
.save-button {
background: #4285f4;
color: white;
}
@media (prefers-color-scheme: dark) {
.cancel-button {
background: #555;
color: #eee;
}
}
/* Mac选项 */
.mac-options {
display: flex;
gap: 15px;
margin-top: 10px;
}
.mac-option {
display: flex;
align-items: center;
gap: 5px;
}
`);
log('样式已添加');
} catch (error) {
handleError(error, '添加样式');
}
}
// 获取启用的搜索引擎
function getEnabledEngines() {
try {
const settings = loadSettings();
const enabledEngineNames = settings.enabledEngines || defaultEngines.map(e => e.name);
return defaultEngines.filter(engine =>
enabledEngineNames.includes(engine.name)
);
} catch (error) {
handleError(error, '获取启用的搜索引擎');
return defaultEngines;
}
}
// 加载设置
function loadSettings() {
try {
const defaultSettings = {
enabledEngines: defaultEngines.map(engine => engine.name),
collapsed: false,
keyboardShortcuts: true,
macModifierType: 'command'
};
const savedSettings = GM_getValue('searchEngineSettings');
if (!savedSettings) {
log('未找到保存的设置,使用默认设置');
return defaultSettings;
}
try {
const parsedSettings = typeof savedSettings === 'string'
? JSON.parse(savedSettings)
: savedSettings;
log('成功加载设置:', parsedSettings);
return {...defaultSettings, ...parsedSettings};
} catch (e) {
handleError(e, '解析设置');
return defaultSettings;
}
} catch (error) {
handleError(error, '加载设置');
return {
enabledEngines: defaultEngines.map(engine => engine.name),
collapsed: false,
keyboardShortcuts: true,
macModifierType: 'command'
};
}
}
// 保存设置
function saveSettings(settings) {
try {
log('保存设置:', settings);
GM_setValue('searchEngineSettings', settings);
return true;
} catch (error) {
handleError(error, '保存设置');
return false;
}
}
// 获取当前搜索引擎和查询词
function getCurrentEngineAndQuery() {
try {
const url = window.location.href;
let currentEngine = null;
let query = '';
// 遍历搜索引擎进行匹配
for (const engine of defaultEngines) {
// 创建正则表达式
const regexPattern = new RegExp(engine.matchPattern, 'i');
if (regexPattern.test(url)) {
log(`匹配到搜索引擎: ${engine.name}`);
currentEngine = engine;
// 提取查询词
const urlObj = new URL(url);
query = urlObj.searchParams.get(engine.queryParam) || '';
// 特殊处理DuckDuckGo
if (engine.name === 'DuckDuckGo' && !query) {
// 检查URL中是否包含q=参数(可能在hash中)
if (url.includes('q=')) {
const match = url.match(/[?&#]q=([^&#]*)/);
if (match && match[1]) {
query = decodeURIComponent(match[1]);
}
}
}
break;
}
}
if (currentEngine) {
log(`当前引擎: ${currentEngine.name}, 查询词: ${query}`);
} else {
log('未匹配到搜索引擎');
}
return { currentEngine, query };
} catch (error) {
handleError(error, '获取当前搜索引擎和查询词');
return { currentEngine: null, query: '' };
}
}
// 切换到指定搜索引擎
function switchToEngine(engine, query) {
try {
if (!query) {
log('没有查询词,取消切换');
return;
}
// 将查询参数编码并替换到URL中
const encodedQuery = encodeURIComponent(query);
const targetUrl = engine.url.replace('{query}', encodedQuery);
log(`切换到 ${engine.name}, URL: ${targetUrl}`);
// 导航到新的URL
window.location.href = targetUrl;
} catch (error) {
handleError(error, '切换搜索引擎');
}
}
// 创建搜索引擎切换器UI
function createSwitcherUI() {
try {
// 检查当前页面是否为搜索结果页
const { currentEngine, query } = getCurrentEngineAndQuery();
if (!currentEngine || !query) {
log('未检测到搜索引擎或查询词,不创建UI');
return;
}
// 检查是否已存在搜索引擎切换器,如果存在则移除
let switcher = document.getElementById('search-engine-switcher');
if (switcher) {
log('搜索引擎切换器已存在,移除旧的');
switcher.remove();
}
// 获取设置
const settings = loadSettings();
// 获取启用的搜索引擎
const enabledEngines = getEnabledEngines();
// 创建搜索引擎切换器容器
switcher = document.createElement('div');
switcher.id = 'search-engine-switcher';
if (settings.collapsed) {
switcher.classList.add('collapsed');
}
// 创建标题栏
const header = document.createElement('div');
header.className = 'switcher-header';
// 创建标题
const title = document.createElement('div');
title.className = 'switcher-title';
title.textContent = '搜索引擎切换';
header.appendChild(title);
// 创建按钮容器
const headerActions = document.createElement('div');
headerActions.className = 'header-actions';
// 创建设置按钮
const settingsButton = document.createElement('button');
settingsButton.className = 'settings-btn';
settingsButton.title = '设置';
settingsButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>';
settingsButton.addEventListener('click', function(e) {
e.stopPropagation();
showSettingsPanel();
});
headerActions.appendChild(settingsButton);
// 创建折叠/展开按钮
const collapseButton = document.createElement('button');
collapseButton.className = 'collapse-btn';
collapseButton.title = settings.collapsed ? '展开' : '折叠';
collapseButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"></polyline></svg>';
collapseButton.addEventListener('click', function(e) {
e.stopPropagation();
// 切换折叠状态
switcher.classList.toggle('collapsed');
// 更新设置
const newSettings = {...settings, collapsed: switcher.classList.contains('collapsed')};
saveSettings(newSettings);
// 更新按钮提示
this.title = newSettings.collapsed ? '展开' : '折叠';
});
headerActions.appendChild(collapseButton);
header.appendChild(headerActions);
switcher.appendChild(header);
// 创建搜索引擎按钮容器
const engineContainer = document.createElement('div');
engineContainer.className = 'engine-container';
// 创建每个搜索引擎按钮
for (const engine of enabledEngines) {
// 如果是当前搜索引擎,则跳过
if (currentEngine && engine.name === currentEngine.name) {
continue;
}
// 创建按钮
const engineBtn = document.createElement('button');
engineBtn.className = 'engine-btn';
engineBtn.title = `在${engine.name}中搜索 "${query}"`;
engineBtn.addEventListener('click', function() {
switchToEngine(engine, query);
});
// 创建图标
const img = document.createElement('img');
img.src = engine.icon;
img.className = 'engine-icon';
img.alt = engine.name;
img.onerror = function() {
// 如果图标加载失败,使用文字缩写
this.outerHTML = `<div style="width:16px;height:16px;display:flex;align-items:center;justify-content:center;background:#f0f0f0;border-radius:3px;font-size:10px;font-weight:bold;">${engine.name.charAt(0)}</div>`;
};
engineBtn.appendChild(img);
// 添加搜索引擎名称
engineBtn.appendChild(document.createTextNode(engine.name));
// 如果启用快捷键,添加提示
if (settings.keyboardShortcuts) {
const shortcutSpan = document.createElement('span');
shortcutSpan.className = 'shortcut';
shortcutSpan.textContent = `${modifierKey}${engine.shortcut}`;
engineBtn.appendChild(shortcutSpan);
}
engineContainer.appendChild(engineBtn);
}
// 如果没有其他搜索引擎按钮,添加一个提示
if (engineContainer.children.length === 0) {
const noEnginesMsg = document.createElement('div');
noEnginesMsg.textContent = '没有其他可用的搜索引擎';
noEnginesMsg.style.padding = '10px 0';
noEnginesMsg.style.color = '#888';
noEnginesMsg.style.fontSize = '12px';
noEnginesMsg.style.textAlign = 'center';
engineContainer.appendChild(noEnginesMsg);
}
switcher.appendChild(engineContainer);
// 添加到页面
document.body.appendChild(switcher);
// 添加点击事件,防止冒泡
switcher.addEventListener('click', function(e) {
e.stopPropagation();
});
log('搜索引擎切换器UI已创建');
} catch (error) {
handleError(error, '创建搜索引擎切换器UI');
}
}
// 显示设置面板
function showSettingsPanel() {
try {
log('显示设置面板');
// 获取当前设置
const settings = loadSettings();
// 创建设置覆盖层
const settingsOverlay = document.createElement('div');
settingsOverlay.className = 'settings-overlay';
// 创建设置容器
const settingsContainer = document.createElement('div');
settingsContainer.className = 'settings-container';
// 创建设置标题
const settingsHeader = document.createElement('div');
settingsHeader.className = 'settings-header';
settingsHeader.textContent = '搜索引擎切换工具设置';
settingsContainer.appendChild(settingsHeader);
// 创建搜索引擎选择部分
const enginesSection = document.createElement('div');
enginesSection.className = 'settings-section';
const enginesTitle = document.createElement('div');
enginesTitle.className = 'section-title';
enginesTitle.textContent = '选择要显示的搜索引擎:';
enginesSection.appendChild(enginesTitle);
const checkboxGroup = document.createElement('div');
checkboxGroup.className = 'checkbox-group';
// 添加每个搜索引擎的复选框
defaultEngines.forEach(engine => {
const label = document.createElement('label');
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.name = 'engine';
checkbox.value = engine.name;
checkbox.checked = settings.enabledEngines.includes(engine.name);
label.appendChild(checkbox);
label.appendChild(document.createTextNode(` ${engine.name}`));
checkboxGroup.appendChild(label);
});
enginesSection.appendChild(checkboxGroup);
settingsContainer.appendChild(enginesSection);
// 创建快捷键部分
const shortcutSection = document.createElement('div');
shortcutSection.className = 'settings-section';
const shortcutTitle = document.createElement('div');
shortcutTitle.className = 'section-title';
shortcutTitle.textContent = '快捷键设置:';
shortcutSection.appendChild(shortcutTitle);
const shortcutCheckbox = document.createElement('label');
const shortcutInput = document.createElement('input');
shortcutInput.type = 'checkbox';
shortcutInput.name = 'keyboardShortcuts';
shortcutInput.checked = settings.keyboardShortcuts;
shortcutCheckbox.appendChild(shortcutInput);
shortcutCheckbox.appendChild(document.createTextNode(' 启用键盘快捷键'));
shortcutSection.appendChild(shortcutCheckbox);
// Mac特定选项
if (isMac) {
const shortcutContent = document.createElement('div');
shortcutContent.style.marginTop = '10px';
shortcutContent.style.marginLeft = '20px';
shortcutContent.style.display = settings.keyboardShortcuts ? 'block' : 'none';
shortcutInput.addEventListener('change', function() {
shortcutContent.style.display = this.checked ? 'block' : 'none';
});
const shortcutDescription = document.createElement('div');
shortcutDescription.textContent = '选择快捷键修饰键:';
shortcutContent.appendChild(shortcutDescription);
const macOptions = document.createElement('div');
macOptions.className = 'mac-options';
// Command选项
const commandOption = document.createElement('label');
commandOption.className = 'mac-option';
const commandRadio = document.createElement('input');
commandRadio.type = 'radio';
commandRadio.name = 'macModifier';
commandRadio.value = 'command';
commandRadio.checked = settings.macModifierType === 'command';
commandOption.appendChild(commandRadio);
commandOption.appendChild(document.createTextNode(' ⌘ (Command)'));
// Option选项
const optionOption = document.createElement('label');
optionOption.className = 'mac-option';
const optionRadio = document.createElement('input');
optionRadio.type = 'radio';
optionRadio.name = 'macModifier';
optionRadio.value = 'option';
optionRadio.checked = settings.macModifierType === 'option';
optionOption.appendChild(optionRadio);
optionOption.appendChild(document.createTextNode(' ⌥ (Option)'));
macOptions.appendChild(commandOption);
macOptions.appendChild(optionOption);
shortcutContent.appendChild(macOptions);
shortcutSection.appendChild(shortcutContent);
} else {
const shortcutDescription = document.createElement('div');
shortcutDescription.style.marginTop = '10px';
shortcutDescription.style.marginLeft = '20px';
shortcutDescription.style.display = settings.keyboardShortcuts ? 'block' : 'none';
shortcutDescription.textContent = '按下 Alt + 快捷键字母 可快速切换搜索引擎';
shortcutInput.addEventListener('change', function() {
shortcutDescription.style.display = this.checked ? 'block' : 'none';
});
shortcutSection.appendChild(shortcutDescription);
}
settingsContainer.appendChild(shortcutSection);
// 创建折叠选项
const collapseSection = document.createElement('div');
collapseSection.className = 'settings-section';
const collapseCheckbox = document.createElement('label');
const collapseInput = document.createElement('input');
collapseInput.type = 'checkbox';
collapseInput.name = 'collapsed';
collapseInput.checked = settings.collapsed;
collapseCheckbox.appendChild(collapseInput);
collapseCheckbox.appendChild(document.createTextNode(' 默认折叠'));
collapseSection.appendChild(collapseCheckbox);
settingsContainer.appendChild(collapseSection);
// 创建按钮
const actions = document.createElement('div');
actions.className = 'settings-actions';
const cancelButton = document.createElement('button');
cancelButton.className = 'settings-button cancel-button';
cancelButton.textContent = '取消';
cancelButton.addEventListener('click', function() {
settingsOverlay.remove();
});
const saveButton = document.createElement('button');
saveButton.className = 'settings-button save-button';
saveButton.textContent = '保存';
saveButton.addEventListener('click', function() {
// 收集设置
const newSettings = {
enabledEngines: Array.from(
settingsContainer.querySelectorAll('input[name="engine"]:checked')
).map(checkbox => checkbox.value),
keyboardShortcuts: settingsContainer.querySelector('input[name="keyboardShortcuts"]').checked,
collapsed: settingsContainer.querySelector('input[name="collapsed"]').checked
};
// Mac特定选项
if (isMac) {
const macModifier = settingsContainer.querySelector('input[name="macModifier"]:checked');
newSettings.macModifierType = macModifier ? macModifier.value : 'command';
}
// 保存设置
const success = saveSettings(newSettings);
if (success) {
settingsOverlay.remove();
// 刷新切换器界面
const switcher = document.getElementById('search-engine-switcher');
if (switcher) {
switcher.remove();
}
setTimeout(createSwitcherUI, 100);
}
});
actions.appendChild(cancelButton);
actions.appendChild(saveButton);
settingsContainer.appendChild(actions);
settingsOverlay.appendChild(settingsContainer);
document.body.appendChild(settingsOverlay);
// 点击外部区域关闭设置面板
settingsOverlay.addEventListener('click', function(e) {
if (e.target === settingsOverlay) {
settingsOverlay.remove();
}
});
log('设置面板已显示');
} catch (error) {
handleError(error, '显示设置面板');
}
}
// 设置键盘快捷键
function setupKeyboardShortcuts() {
try {
const settings = loadSettings();
if (!settings.keyboardShortcuts) {
log('快捷键功能已禁用');
return;
}
const { currentEngine, query } = getCurrentEngineAndQuery();
if (!query) {
log('没有查询词,不设置快捷键');
return;
}
log('设置键盘快捷键');
document.addEventListener('keydown', function(e) {
// 检查修饰键是否按下
let modifierPressed = false;
if (isMac) {
if (settings.macModifierType === 'command') {
modifierPressed = e.metaKey;
} else if (settings.macModifierType === 'option') {
modifierPressed = e.altKey;
} else {
modifierPressed = e.metaKey;
}
} else {
modifierPressed = e.altKey;
}
if (!modifierPressed) return;
// 获取按下的键
const key = e.key.toUpperCase();
// 查找匹配的搜索引擎
const targetEngine = getEnabledEngines().find(engine =>
engine.shortcut && engine.shortcut.toUpperCase() === key &&
(!currentEngine || engine.name !== currentEngine.name)
);
if (targetEngine) {
e.preventDefault();
switchToEngine(targetEngine, query);
}
});
log('键盘快捷键已设置');
} catch (error) {
handleError(error, '设置键盘快捷键');
}
}
// 设置DOM变化观察器
function setupMutationObserver() {
try {
log('设置MutationObserver开始');
// 创建一个观察器实例
const observer = new MutationObserver(function(mutations) {
// 检查是否需要重新创建UI
const switcher = document.getElementById('search-engine-switcher');
if (!switcher) {
log('MutationObserver: UI不存在,重新创建');
createSwitcherUI();
}
});
// 更频繁地观察DOM变化
const config = { childList: true, subtree: true };
observer.observe(document.body, config);
// 定期检查UI是否存在
setInterval(() => {
const switcher = document.getElementById('search-engine-switcher');
if (!switcher) {
log('定期检查: UI不存在,重新创建');
createSwitcherUI();
}
}, 3000);
log('MutationObserver已增强设置');
} catch (error) {
handleError(error, '设置MutationObserver');
}
}
// 添加菜单项
function setupUserMenu() {
try {
GM_registerMenuCommand('搜索引擎切换工具设置', showSettingsPanel);
log('用户菜单已设置');
} catch (error) {
handleError(error, '设置用户菜单');
}
}
// 初始化
function initialize() {
try {
log('开始初始化搜索引擎切换助手');
addStyles();
setupUserMenu();
setTimeout(createSwitcherUI, 100);
setupKeyboardShortcuts();
setupMutationObserver();
log('初始化完成');
} catch (error) {
handleError(error, '初始化');
}
}
// 立即执行初始化
initialize();
// 确保DOM加载后执行
if (document.readyState !== 'complete') {
document.addEventListener('DOMContentLoaded', function() {
setTimeout(createSwitcherUI, 300);
});
}
// 页面完全加载后再次执行
window.addEventListener('load', function() {
setTimeout(createSwitcherUI, 500);
});
})();