// ==UserScript== // @name 聚合搜索引擎切换(自用) // @namespace http://tampermonkey.net/ // @version v1.15 // @description 在页面底部显示一个聚合搜索引擎切换导航,支持自定义引擎和拖拽排序 // @author 晚风知我意 // @match *://*/*keyword=* // @match *://*/*query=* // @match *://*/*word=* // @match *://*/*text=* // @match *://*/*key=* // @match *://*/*web=* // @match *://*/*wd=* // @match *://*/*kw=* // @match *://*/*q=* // @match *://*/*p=* // @grant unsafeWindow // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @run-at document-body // @license MIT // ==/UserScript== const punkDeafultMark = "Google-Bing-Baidu-MetaSo-YandexSearch-Bilibili-ApkPure-Quark-Zhihu"; const defaultSearchEngines = [ { name: "谷歌", searchUrl: "https://www.google.com/search?q={keyword}", searchkeyName: ["q"], matchUrl: /google\.com.*?search.*?q=/g, mark: "Google", svgCode: ` ` }, { name: "必应", searchUrl: "https://www.bing.com/search?q={keyword}", searchkeyName: ["q"], matchUrl: /bing\.com.*?search\?q=?/g, mark: "Bing", svgCode: ` ` }, { name: "百度", searchUrl: "https://www.baidu.com/s?wd={keyword}", searchkeyName: ["wd", "word"], matchUrl: /baidu\.com.*?w(or)?d=?/g, mark: "Baidu", svgCode: ` ` }, { name: "密塔", searchUrl: "https://metaso.cn/?s=itab1&q={keyword}", searchkeyName: ["q"], matchUrl: /metaso\.cn.*?q=/g, mark: "MetaSo", svgCode: ` ` }, { name: "Yandex", searchUrl: "https://yandex.com/search/?text={keyword}", searchkeyName: ["text"], matchUrl: /yandex\.com.*?text=/g, mark: "YandexSearch", svgCode: ` ` }, { name: "ApkPure", searchUrl: "https://apkpure.com/search?q={keyword}", searchkeyName: ["q"], matchUrl: /apkpure\.com.*?q=?/g, mark: "ApkPure", svgCode: ` ` }, { name: "哔哩哔哩", searchUrl: "https://m.bilibili.com/search?keyword={keyword}", searchkeyName: ["keyword"], matchUrl: /bilibili\.com.*?keyword=/g, mark: "Bilibili", svgCode: ` ` }, { name: "夸克", searchUrl: "https://quark.sm.cn/s?q={keyword}", searchkeyName: ["q"], matchUrl: /quark\.sm\.cn.*?q=/g, mark: "Quark", svgCode: ` ` }, { name: "知乎", searchUrl: "https://www.zhihu.com/search?type=content&q={keyword}", searchkeyName: ["q"], matchUrl: /zhihu\.com.*?q=/g, mark: "Zhihu", svgCode: ` ` }, { name: "GitHub", searchUrl: "https://github.com/search?q={keyword}", searchkeyName: ["q"], matchUrl: /github\.com.*?search\?q=/, mark: "GitHub", svgCode: ` ` }, { name: "YouTube", searchUrl: "https://www.youtube.com/results?search_query={keyword}", searchkeyName: ["search_query"], matchUrl: "youtube\\.com.*?results\\?search_query=", mark: "YouTube", svgCode: ` ` }, { name: "淘宝", searchUrl: "https://s.taobao.com/search?q={keyword}", searchkeyName: ["q"], matchUrl: "taobao\\.com.*?search\\?q=", mark: "TaoBao", svgCode: ` ` }, { name: "PubMed", searchUrl: "https://pubmed.ncbi.nlm.nih.gov/?term={keyword}", searchkeyName: ["term"], matchUrl: "pubmed\\.ncbi\\.nlm\\.nih\\.gov.*?term={keyword}", mark: "PubMed", svgCode: ` ` }, { name: "DuckDuckGo", searchUrl: "https://duckduckgo.com/?q={keyword}", searchkeyName: ["q"], matchUrl: "duckduckgo\\.com.*?q={keyword}", mark: "DuckDuckGo", svgCode: ` ` }, { name: "搜狗", searchUrl: "https://www.sogou.com/web?query={keyword}", searchkeyName: ["query"], matchUrl: /sogou\.com.*?query=/g, mark: "Sogou", svgCode: ` Sogou icon` }, { name: "360搜索", searchUrl: "https://www.so.com/s?q={keyword}", searchkeyName: ["q"], matchUrl: /so\.com.*?q=/g, mark: "360Search", svgCode: `` }, { name: "Startpage", searchUrl: "https://www.startpage.com/sp/search?query={keyword}", searchkeyName: ["query"], matchUrl: /startpage\.com.*?query=/g, mark: "Startpage", svgCode: `` }, { name: "WolframAlpha", searchUrl: "https://www.wolframalpha.com/input?i={keyword}", searchkeyName: ["i"], matchUrl: /wolframalpha\.com.*?i=/g, mark: "WolframAlpha", svgCode: `` }, { name: "谷歌学术", searchUrl: "https://scholar.google.com/scholar?q={keyword}", searchkeyName: ["q"], matchUrl: /scholar\.google\..*?q=/g, mark: "GoogleScholar", svgCode: `` }, { name: "百度学术", searchUrl: "https://xueshu.baidu.com/s?wd={keyword}", searchkeyName: ["wd"], matchUrl: /xueshu\.baidu\.com.*?wd=/g, mark: "BaiduScholar", svgCode: `` }, { name: "CNKI", searchUrl: "https://search.cnki.net/search.aspx?q={keyword}", searchkeyName: ["q"], matchUrl: /cnki\.net.*?q=/g, mark: "CNKI", svgCode: `` }, { name: "StackOverflow", searchUrl: "https://stackoverflow.com/search?q={keyword}", searchkeyName: ["q"], matchUrl: /stackoverflow\.com.*?search\?q=/g, mark: "StackOverflow", svgCode: `` }, { name: "MDN", searchUrl: "https://developer.mozilla.org/zh-CN/search?q={keyword}", searchkeyName: ["q"], matchUrl: /developer\.mozilla\.org.*?q=/g, mark: "MDN", svgCode: `` }, { name: "Coursera", searchUrl: "https://www.coursera.org/search?query={keyword}", searchkeyName: ["query"], matchUrl: /coursera\.org.*?query=/g, mark: "Coursera", svgCode: `` }, { name: "京东", searchUrl: "https://search.jd.com/Search?keyword={keyword}", searchkeyName: ["keyword"], matchUrl: /jd\.com.*?keyword=/g, mark: "JD", svgCode: `` }, { name: "亚马逊", searchUrl: "https://www.amazon.com/s?k={keyword}", searchkeyName: ["k"], matchUrl: /amazon\..*?k=/g, mark: "Amazon", svgCode: `` }, { name: "AliExpress", searchUrl: "https://www.aliexpress.com/wholesale?SearchText={keyword}", searchkeyName: ["SearchText"], matchUrl: /aliexpress\.com.*?SearchText=/g, mark: "AliExpress", svgCode: `` }, { name: "微博", searchUrl: "https://s.weibo.com/weibo?q={keyword}", searchkeyName: ["q"], matchUrl: /weibo\.com.*?q=/g, mark: "Weibo", svgCode: `` }, { name: "抖音", searchUrl: "https://www.douyin.com/search/{keyword}", searchkeyName: ["keyword"], matchUrl: /douyin\.com.*?search/g, mark: "Douyin", svgCode: `` }, { name: "小红书", searchUrl: "https://www.xiaohongshu.com/search_result?keyword={keyword}", searchkeyName: ["keyword"], matchUrl: /xiaohongshu\.com.*?keyword=/g, mark: "Xiaohongshu", svgCode: `` }, { name: "豆瓣", searchUrl: "https://www.douban.com/search?q={keyword}", searchkeyName: ["q"], matchUrl: /douban\.com.*?q=/g, mark: "Douban", svgCode: `` }, { name: "IMDb", searchUrl: "https://www.imdb.com/find?q={keyword}", searchkeyName: ["q"], matchUrl: /imdb\.com.*?q=/g, mark: "IMDb", svgCode: `` }, { name: "RottenTomatoes", searchUrl: "https://www.rottentomatoes.com/search?search={keyword}", searchkeyName: ["search"], matchUrl: /rottentomatoes\.com.*?search=/g, mark: "RottenTomatoes", svgCode: `` }, { name: "Steam", searchUrl: "https://store.steampowered.com/search/?term={keyword}", searchkeyName: ["term"], matchUrl: /steampowered\.com.*?term=/g, mark: "Steam", svgCode: `` }, { name: "Spotify", searchUrl: "https://open.spotify.com/search/{keyword}", searchkeyName: ["q"], matchUrl: /open\.spotify\.com.*?search/g, mark: "Spotify", svgCode: `` }, { name: "网易云音乐", searchUrl: "https://music.163.com/#/search/m/?s={keyword}", searchkeyName: ["s"], matchUrl: /music\.163\.com.*?s=/g, mark: "NeteaseMusic", svgCode: `` }, { name: "Pinterest", searchUrl: "https://www.pinterest.com/search/pins/?q={keyword}", searchkeyName: ["q"], matchUrl: /pinterest\..*?q=/g, mark: "Pinterest", svgCode: `` }, { name: "Flickr", searchUrl: "https://www.flickr.com/search/?text={keyword}", searchkeyName: ["text"], matchUrl: /flickr\.com.*?text=/g, mark: "Flickr", svgCode: `` }, { name: "维基百科", searchUrl: "https://zh.wikipedia.org/w/index.php?search={keyword}", searchkeyName: ["search"], matchUrl: /wikipedia\.org.*?search=/g, mark: "Wikipedia", svgCode: `` }, { name: "ArchWiki", searchUrl: "https://wiki.archlinux.org/index.php?search={keyword}", searchkeyName: ["search"], matchUrl: /archlinux\.org.*?search=/g, mark: "ArchWiki", svgCode: `` }, { name: "微信读书", searchUrl: "https://weread.qq.com/web/search/books?keyword={keyword}", searchkeyName: ["keyword"], matchUrl: /weread\.qq\.com.*?keyword=/g, mark: "WeRead", svgCode: `` }, { name: "天眼查", searchUrl: "https://www.tianyancha.com/search?key={keyword}", searchkeyName: ["key"], matchUrl: /tianyancha\.com.*?key=/g, mark: "Tianyancha", svgCode: `` }, { name: "Ecosia", searchUrl: "https://www.ecosia.org/search?q={keyword}", searchkeyName: ["q"], matchUrl: "ecosia\\.org.*?search\\?q=", mark: "Ecosia", svgCode: ` ` }, ]; let userSearchEngines = GM_getValue("userSearchEngines", []); let searchUrlMap = [...defaultSearchEngines, ...userSearchEngines]; let lastScrollTop = 0; let punkJetBoxVisible = true; let currentInput = ""; let scriptLoaded = false; let containerAdded = false; let longPressTimer = null; let currentDraggedButton = null; let hasUnsavedChanges = false; // 检查容器是否已存在 function engineContainerExists() { return document.querySelector('.engine-container') !== null; } function isValidScope() { return searchUrlMap.some(item => window.location.href.match(item.matchUrl) != null); } function getKeywords() { try { let keywords = ""; for (let urlItem of searchUrlMap) { if (window.location.href.match(urlItem.matchUrl) != null) { for (let keyItem of urlItem.searchkeyName) { if (window.location.href.indexOf(keyItem) >= 0) { let url = new URL(window.location.href); keywords = url.searchParams.get(keyItem); if (keywords) { localStorage.setItem("last_successful_keywords", keywords); sessionStorage.setItem("last_successful_keywords", keywords); } return keywords; } } } } return sessionStorage.getItem("last_successful_keywords") || localStorage.getItem("last_successful_keywords") || ""; } catch (error) { console.error("获取关键词时出错:", error.message, "当前URL:", window.location.href); return ""; } } function monitorInputFields() { const inputFields = document.querySelectorAll('input[type="text"], textarea'); if (inputFields.length > 0) { inputFields.forEach(input => { input.addEventListener('input', (event) => { currentInput = event.target.value; sessionStorage.setItem("currentInput", currentInput); }); }); } const observer = new MutationObserver(() => { const newInputFields = document.querySelectorAll('input[type="text"], textarea'); newInputFields.forEach(input => { if (!input.dataset.monitored) { input.dataset.monitored = true; input.addEventListener('input', (event) => { currentInput = event.target.value; sessionStorage.setItem("currentInput", currentInput); }); } }); }); observer.observe(document.body, { childList: true, subtree: true }); } function updateSearchBoxPosition() { const punkJetBox = document.getElementById("punkjet-search-box"); if (!punkJetBox) return; punkJetBox.style.bottom = "0px"; punkJetBox.style.left = "2%"; punkJetBox.style.width = "96%"; } // 从当前页面获取搜索引擎信息 function extractSearchEngineFromPage() { const searchForms = document.querySelectorAll('form[action*="search"], form[action*="query"], form[action*="find"]'); const searchInputs = document.querySelectorAll('input[type="search"], input[name*="search"], input[name*="query"], input[name*="q"]'); let searchInfo = { name: "", searchUrl: "", searchkeyName: [], matchUrl: "", mark: "", found: false }; if (searchForms.length > 0) { const form = searchForms[0]; const action = form.getAttribute('action') || ''; const method = (form.getAttribute('method') || 'get').toLowerCase(); if (action) { let baseUrl = action; if (!action.startsWith('http')) { baseUrl = new URL(action, window.location.origin).href; } const inputs = form.querySelectorAll('input[name]'); let keyParam = ''; for (let input of inputs) { const name = input.getAttribute('name'); if (name && (name.includes('q') || name.includes('search') || name.includes('query') || name.includes('keyword'))) { keyParam = name; break; } } if (!keyParam && searchInputs.length > 0) { keyParam = searchInputs[0].getAttribute('name') || 'q'; } if (keyParam) { searchInfo.searchUrl = method === 'post' ? `${baseUrl}?${keyParam}={keyword}` : `${baseUrl}${baseUrl.includes('?') ? '&' : '?'}${keyParam}={keyword}`; searchInfo.searchkeyName = [keyParam]; searchInfo.found = true; const domain = new URL(baseUrl).hostname.replace('www.', ''); searchInfo.name = domain.split('.')[0].charAt(0).toUpperCase() + domain.split('.')[0].slice(1); searchInfo.mark = domain.replace(/\./g, '_'); searchInfo.matchUrl = `.*${domain}.*`; } } } if (!searchInfo.found && searchInputs.length > 0) { const input = searchInputs[0]; const name = input.getAttribute('name') || 'q'; const domain = window.location.hostname.replace('www.', ''); searchInfo.searchUrl = `${window.location.origin}/search?${name}={keyword}`; searchInfo.searchkeyName = [name]; searchInfo.name = domain.split('.')[0].charAt(0).toUpperCase() + domain.split('.')[0].slice(1); searchInfo.mark = domain.replace(/\./g, '_'); searchInfo.matchUrl = `.*${domain}.*`; searchInfo.found = true; } return searchInfo; } function createManagementPanel() { const panel = document.createElement("div"); panel.id = "engine-management-panel"; panel.style.position = "fixed"; panel.style.top = "50%"; panel.style.left = "50%"; panel.style.transform = "translate(-50%, -50%)"; panel.style.width = "90%"; panel.style.maxWidth = "800px"; panel.style.height = "90vh"; panel.style.maxHeight = "90vh"; panel.style.backgroundColor = "#ffffff"; panel.style.borderRadius = "15px"; panel.style.boxShadow = "0 10px 30px rgba(0,0,0,0.3)"; panel.style.padding = "0"; panel.style.zIndex = "10000"; panel.style.display = "none"; panel.style.overflow = "hidden"; panel.style.fontFamily = "'Segoe UI', Tahoma, Geneva, Verdana, sans-serif"; panel.style.display = "flex"; panel.style.flexDirection = "column"; panel.style.boxSizing = "border-box"; // 顶栏 - 固定高度 15vh const header = document.createElement("div"); header.style.height = "15vh"; header.style.minHeight = "80px"; header.style.maxHeight = "120px"; header.style.backgroundColor = "#2c3e50"; header.style.color = "white"; header.style.padding = "20px"; header.style.borderRadius = "15px 15px 0 0"; header.style.position = "relative"; header.style.boxSizing = "border-box"; header.style.flexShrink = "0"; const title = document.createElement("h2"); title.textContent = "📊 搜索引擎管理中心"; title.style.margin = "0"; title.style.fontSize = "1.5em"; title.style.fontWeight = "300"; header.appendChild(title); const subtitle = document.createElement("p"); subtitle.textContent = "管理您的搜索快捷方式"; subtitle.style.margin = "5px 0 0 0"; subtitle.style.opacity = "0.8"; subtitle.style.fontSize = "0.9em"; header.appendChild(subtitle); const unsavedIndicator = document.createElement("div"); unsavedIndicator.id = "unsaved-indicator"; unsavedIndicator.textContent = "● 有未保存的更改"; unsavedIndicator.style.position = "absolute"; unsavedIndicator.style.top = "15px"; unsavedIndicator.style.right = "20px"; unsavedIndicator.style.color = "#e74c3c"; unsavedIndicator.style.fontSize = "0.8em"; unsavedIndicator.style.display = "none"; header.appendChild(unsavedIndicator); panel.appendChild(header); // 主内容区域 - 固定高度 65vh const content = document.createElement("div"); content.style.height = "65vh"; content.style.minHeight = "300px"; content.style.position = "relative"; content.style.overflow = "hidden"; content.style.padding = "0"; content.style.boxSizing = "border-box"; content.style.display = "flex"; content.style.flexDirection = "column"; content.style.flexShrink = "0"; // 快速操作栏 const quickActions = document.createElement("div"); quickActions.style.padding = "20px"; quickActions.style.display = "flex"; quickActions.style.gap = "10px"; quickActions.style.flexWrap = "wrap"; quickActions.style.justifyContent = "space-between"; quickActions.style.backgroundColor = "#ffffff"; quickActions.style.borderBottom = "1px solid #ecf0f1"; quickActions.style.boxSizing = "border-box"; quickActions.style.flexShrink = "0"; const leftActionGroup = document.createElement("div"); leftActionGroup.style.display = "flex"; leftActionGroup.style.gap = "10px"; leftActionGroup.style.flexWrap = "wrap"; const extractBtn = createActionButton("🌐 自动添加", "#3498db", "自动识别当前页面的搜索引擎"); const addBtn = createActionButton("➕ 手动添加", "#27ae60", "手动添加新的搜索引擎"); leftActionGroup.appendChild(extractBtn); leftActionGroup.appendChild(addBtn); const rightActionGroup = document.createElement("div"); rightActionGroup.style.display = "flex"; rightActionGroup.style.gap = "10px"; rightActionGroup.style.flexWrap = "wrap"; const saveBtn = document.createElement("button"); saveBtn.id = "panel-save-btn"; saveBtn.innerHTML = "💾 保存设置"; saveBtn.title = "保存当前设置"; saveBtn.style.cssText = ` padding: 10px 20px; background: #95a5a6; color: white; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 600; display: flex; align-items: center; gap: 5px; transition: all 0.3s ease; opacity: 0.7; pointer-events: none; min-width: 120px; justify-content: center; `; const resetBtn = createActionButton("🔄 恢复默认", "#e74c3c", "恢复默认搜索引擎设置"); rightActionGroup.appendChild(saveBtn); rightActionGroup.appendChild(resetBtn); quickActions.appendChild(leftActionGroup); quickActions.appendChild(rightActionGroup); content.appendChild(quickActions); // 列表区域 - 可滚动 const listSection = document.createElement("div"); listSection.style.flex = "1"; listSection.style.overflow = "hidden"; listSection.style.padding = "0 20px"; listSection.style.boxSizing = "border-box"; listSection.style.display = "flex"; listSection.style.flexDirection = "column"; listSection.style.overflow = "auto" const listTitle = document.createElement("h3"); listTitle.textContent = "📋 已配置的搜索引擎"; listTitle.style.color = "#2c3e50"; listTitle.style.margin = "15px 0"; listTitle.style.fontWeight = "500"; listTitle.style.flexShrink = "0"; listSection.appendChild(listTitle); const engineList = document.createElement("div"); engineList.id = "engine-management-list"; engineList.style.flex = "1"; engineList.style.overflowY = "auto"; engineList.style.overflowX = "hidden"; engineList.style.display = "grid"; engineList.style.gap = "10px"; engineList.style.gridTemplateColumns = "repeat(auto-fill, minmax(300px, 1fr))"; engineList.style.paddingBottom = "10px"; engineList.style.boxSizing = "border-box"; listSection.appendChild(engineList); content.appendChild(listSection); // 添加引擎表单区域 const formSection = document.createElement("div"); formSection.id = "add-engine-form"; formSection.style.display = "none"; formSection.style.backgroundColor = "#f8f9fa"; formSection.style.padding = "20px"; formSection.style.borderRadius = "10px"; formSection.style.margin = "10px 0"; formSection.style.boxSizing = "border-box"; formSection.style.flexShrink = "0"; const formTitle = document.createElement("h3"); formTitle.textContent = "✨ 添加新搜索引擎"; formTitle.style.color = "#2c3e50"; formTitle.style.marginBottom = "15px"; formSection.appendChild(formTitle); const form = document.createElement("div"); form.style.display = "grid"; form.style.gap = "15px"; form.style.gridTemplateColumns = "1fr 1fr"; const fields = [ { label: "引擎名称", placeholder: "例如: Google", type: "text", id: "engine-name", required: true }, { label: "唯一标识", placeholder: "例如: google", type: "text", id: "engine-mark", required: true }, { label: "搜索URL", placeholder: "使用 {keyword} 作为占位符", type: "text", id: "engine-url", required: true, fullWidth: true }, { label: "关键词参数", placeholder: "例如: q,query,search", type: "text", id: "engine-keys", required: true, fullWidth: true } ]; fields.forEach(field => { const container = document.createElement("div"); if (field.fullWidth) { container.style.gridColumn = "1 / -1"; } const label = document.createElement("label"); label.textContent = field.label; label.style.display = "block"; label.style.marginBottom = "5px"; label.style.fontWeight = "500"; label.style.color = "#34495e"; container.appendChild(label); const input = document.createElement("input"); input.type = field.type; input.placeholder = field.placeholder; input.id = field.id; input.required = field.required; input.style.width = "100%"; input.style.padding = "10px"; input.style.border = "1px solid #ddd"; input.style.borderRadius = "5px"; input.style.fontSize = "14px"; container.appendChild(input); form.appendChild(container); }); const iconContainer = document.createElement("div"); iconContainer.style.gridColumn = "1 / -1"; const iconTitle = document.createElement("h4"); iconTitle.textContent = "🎨 图标设置"; iconTitle.style.marginBottom = "10px"; iconTitle.style.color = "#34495e"; iconContainer.appendChild(iconTitle); const iconGrid = document.createElement("div"); iconGrid.style.display = "grid"; iconGrid.style.gridTemplateColumns = "1fr 2fr 1fr"; iconGrid.style.gap = "10px"; iconGrid.style.alignItems = "end"; const typeGroup = document.createElement("div"); const typeLabel = document.createElement("label"); typeLabel.textContent = "图标类型"; typeLabel.style.display = "block"; typeLabel.style.marginBottom = "5px"; typeLabel.style.fontWeight = "500"; typeGroup.appendChild(typeLabel); const iconTypeSelect = document.createElement("select"); iconTypeSelect.id = "icon-type"; iconTypeSelect.style.width = "100%"; iconTypeSelect.style.padding = "10px"; iconTypeSelect.style.border = "1px solid #ddd"; iconTypeSelect.style.borderRadius = "5px"; ["svg", "image", "text", "emoji"].forEach(type => { const option = document.createElement("option"); option.value = type; option.textContent = type.charAt(0).toUpperCase() + type.slice(1); iconTypeSelect.appendChild(option); }); typeGroup.appendChild(iconTypeSelect); iconGrid.appendChild(typeGroup); const inputGroup = document.createElement("div"); const inputLabel = document.createElement("label"); inputLabel.textContent = "图标内容"; inputLabel.style.display = "block"; inputLabel.style.marginBottom = "5px"; inputLabel.style.fontWeight = "500"; inputGroup.appendChild(inputLabel); const iconInput = document.createElement("input"); iconInput.type = "text"; iconInput.id = "icon-input"; iconInput.placeholder = "SVG代码、图片URL、文字或表情符号"; iconInput.style.width = "100%"; iconInput.style.padding = "10px"; iconInput.style.border = "1px solid #ddd"; iconInput.style.borderRadius = "5px"; inputGroup.appendChild(iconInput); iconGrid.appendChild(inputGroup); const previewGroup = document.createElement("div"); const previewButton = document.createElement("button"); previewButton.textContent = "预览图标"; previewButton.style.width = "100%"; previewButton.style.padding = "10px"; previewButton.style.backgroundColor = "#3498db"; previewButton.style.color = "white"; previewButton.style.border = "none"; previewButton.style.borderRadius = "5px"; previewButton.style.cursor = "pointer"; previewButton.id = "preview-icon"; previewGroup.appendChild(previewButton); iconGrid.appendChild(previewGroup); iconContainer.appendChild(iconGrid); const previewContainer = document.createElement("div"); previewContainer.style.gridColumn = "1 / -1"; previewContainer.style.marginTop = "15px"; previewContainer.style.textAlign = "center"; const previewLabel = document.createElement("label"); previewLabel.textContent = "图标预览 (推荐比例 8:5)"; previewLabel.style.display = "block"; previewLabel.style.marginBottom = "10px"; previewLabel.style.fontWeight = "500"; previewContainer.appendChild(previewLabel); const iconPreview = document.createElement("div"); iconPreview.id = "icon-preview"; iconPreview.style.width = "88px"; iconPreview.style.height = "55px"; iconPreview.style.border = "2px dashed #bdc3c7"; iconPreview.style.borderRadius = "8px"; iconPreview.style.margin = "0 auto"; iconPreview.style.display = "flex"; iconPreview.style.justifyContent = "center"; iconPreview.style.alignItems = "center"; iconPreview.style.overflow = "hidden"; iconPreview.style.background = "#ecf0f1"; previewContainer.appendChild(iconPreview); iconContainer.appendChild(previewContainer); form.appendChild(iconContainer); formSection.appendChild(form); const formActions = document.createElement("div"); formActions.style.gridColumn = "1 / -1"; formActions.style.display = "flex"; formActions.style.gap = "10px"; formActions.style.marginTop = "20px"; const saveFormBtn = createActionButton("💾 保存引擎", "#27ae60", ""); const cancelFormBtn = createActionButton("❌ 取消", "#95a5a6", ""); formActions.appendChild(saveFormBtn); formActions.appendChild(cancelFormBtn); formSection.appendChild(formActions); // 将表单添加到列表区域 listSection.appendChild(formSection); panel.appendChild(content); // 底栏 - 固定高度 20vh const footer = document.createElement("div"); footer.style.height = "20vh"; footer.style.minHeight = "60px"; footer.style.maxHeight = "90px"; footer.style.backgroundColor = "#ecf0f1"; footer.style.padding = "15px 20px"; footer.style.borderTop = "1px solid #bdc3c7"; footer.style.display = "flex"; footer.style.justifyContent = "space-between"; footer.style.alignItems = "center"; footer.style.boxSizing = "border-box"; footer.style.flexShrink = "0"; footer.style.borderRadius = "0 0 15px 15px"; const selectedCount = document.createElement("span"); selectedCount.id = "selected-count"; selectedCount.textContent = "已选择 0 个引擎"; selectedCount.style.color = "#7f8c8d"; selectedCount.style.fontSize = "0.9em"; footer.appendChild(selectedCount); const footerActions = document.createElement("div"); footerActions.style.display = "flex"; footerActions.style.gap = "10px"; const closeBtn = createActionButton("❌ 关闭", "#95a5a6", ""); footerActions.appendChild(closeBtn); footer.appendChild(footerActions); panel.appendChild(footer); document.body.appendChild(panel); // 事件监听器 extractBtn.addEventListener("click", extractFromCurrentPage); addBtn.addEventListener("click", () => showAddForm(true)); resetBtn.addEventListener("click", resetToDefault); previewButton.addEventListener("click", previewIcon); saveFormBtn.addEventListener("click", saveNewEngine); cancelFormBtn.addEventListener("click", () => showAddForm(false)); saveBtn.addEventListener("click", saveEngineSettings); closeBtn.addEventListener("click", closeManagementPanel); panel.addEventListener("click", (e) => { if (e.target === panel) { closeManagementPanel(); } }); return panel; } function createActionButton(text, color, title) { const button = document.createElement("button"); button.textContent = text; button.title = title; button.style.padding = "10px 15px"; button.style.backgroundColor = color; button.style.color = "white"; button.style.border = "none"; button.style.borderRadius = "8px"; button.style.cursor = "pointer"; button.style.fontSize = "14px"; button.style.minWidth = "120px"; button.style.transition = "all 0.3s ease"; button.addEventListener("mouseenter", () => { button.style.transform = "translateY(-2px)"; button.style.boxShadow = "0 4px 8px rgba(0,0,0,0.2)"; }); button.addEventListener("mouseleave", () => { button.style.transform = "translateY(0)"; button.style.boxShadow = "none"; }); return button; } function closeManagementPanel() { if (hasUnsavedChanges) { if (confirm("⚠️ 您有未保存的更改,确定要关闭吗?")) { document.getElementById("engine-management-panel").style.display = "none"; hasUnsavedChanges = false; } } else { document.getElementById("engine-management-panel").style.display = "none"; } } function markUnsavedChanges() { hasUnsavedChanges = true; const indicator = document.getElementById("unsaved-indicator"); const saveBtn = document.getElementById("panel-save-btn"); if (indicator) { indicator.style.display = "block"; } if (saveBtn) { saveBtn.style.opacity = "1"; saveBtn.style.pointerEvents = "auto"; saveBtn.style.background = "#e67e22"; saveBtn.innerHTML = "💾 保存更改"; saveBtn.addEventListener("mouseenter", function() { this.style.transform = "translateY(-2px)"; this.style.boxShadow = "0 4px 8px rgba(0,0,0,0.2)"; }); saveBtn.addEventListener("mouseleave", function() { this.style.transform = "translateY(0)"; this.style.boxShadow = "none"; }); } } function clearUnsavedChanges() { hasUnsavedChanges = false; const indicator = document.getElementById("unsaved-indicator"); const saveBtn = document.getElementById("panel-save-btn"); if (indicator) { indicator.style.display = "none"; } if (saveBtn) { saveBtn.style.opacity = "0.7"; saveBtn.style.pointerEvents = "none"; saveBtn.style.background = "#95a5a6"; saveBtn.innerHTML = "💾 保存设置"; saveBtn.onmouseenter = null; saveBtn.onmouseleave = null; setTimeout(() => { if (!hasUnsavedChanges) { saveBtn.innerHTML = "✅ 已保存"; saveBtn.style.background = "#27ae60"; setTimeout(() => { if (!hasUnsavedChanges) { saveBtn.innerHTML = "💾 保存设置"; saveBtn.style.background = "#95a5a6"; } }, 2000); } }, 100); } } // 从当前页面提取搜索引擎 function extractFromCurrentPage() { const searchInfo = extractSearchEngineFromPage(); if (!searchInfo.found) { alert("❌ 无法自动识别当前页面的搜索引擎。请手动添加。"); return; } showAddForm(true); document.getElementById("engine-name").value = searchInfo.name; document.getElementById("engine-mark").value = searchInfo.mark; document.getElementById("engine-url").value = searchInfo.searchUrl; document.getElementById("engine-keys").value = searchInfo.searchkeyName.join(","); const favicon = document.querySelector('link[rel*="icon"]'); if (favicon) { const iconUrl = favicon.href; if (!iconUrl.startsWith('data:')) { document.getElementById("icon-type").value = "image"; document.getElementById("icon-input").value = iconUrl; previewIcon(); } } alert(`✅ 已自动识别 ${searchInfo.name} 搜索引擎!请检查并保存。`); } // 显示/隐藏添加表单 function showAddForm(show) { const formSection = document.getElementById("add-engine-form"); const engineList = document.getElementById("engine-management-list"); const listTitle = formSection.previousElementSibling; if (show) { formSection.style.display = "block"; engineList.style.display = "none"; listTitle.style.display = "none"; document.getElementById("engine-name").value = ""; document.getElementById("engine-mark").value = ""; document.getElementById("engine-url").value = ""; document.getElementById("engine-keys").value = ""; document.getElementById("icon-input").value = ""; document.getElementById("icon-preview").innerHTML = ""; } else { formSection.style.display = "none"; engineList.style.display = "grid"; listTitle.style.display = "block"; } } // 预览图标 function previewIcon() { const type = document.getElementById("icon-type").value; const value = document.getElementById("icon-input").value.trim(); const preview = document.getElementById("icon-preview"); preview.innerHTML = ""; preview.style.backgroundImage = "none"; preview.style.backgroundColor = "#ecf0f1"; if (!value) return; try { if (type === "svg") { const parser = new DOMParser(); const svgDoc = parser.parseFromString(value, "image/svg+xml"); if (svgDoc.querySelector("parsererror")) { throw new Error("无效的SVG代码"); } preview.innerHTML = value; } else if (type === "image") { preview.style.backgroundImage = `url(${value})`; preview.style.backgroundSize = "contain"; preview.style.backgroundRepeat = "no-repeat"; preview.style.backgroundPosition = "center"; } else if (type === "text") { preview.textContent = value.length > 4 ? value.substring(0, 4) : value; preview.style.fontSize = value.length > 4 ? "14px" : "18px"; preview.style.color = "#2c3e50"; preview.style.fontWeight = "bold"; } else if (type === "emoji") { preview.textContent = value; preview.style.fontSize = "24px"; } } catch (e) { alert("❌ 图标预览失败: " + e.message); } } // 保存新引擎 function saveNewEngine() { const name = document.getElementById("engine-name").value.trim(); const mark = document.getElementById("engine-mark").value.trim(); const url = document.getElementById("engine-url").value.trim(); const keys = document.getElementById("engine-keys").value.split(',').map(k => k.trim()); const iconType = document.getElementById("icon-type").value; const iconValue = document.getElementById("icon-input").value.trim(); if (!name || !mark || !url || keys.length === 0) { alert("❌ 请填写所有必填字段"); return; } if (searchUrlMap.some(engine => engine.mark === mark)) { alert("❌ 标识已存在,请使用其他标识"); return; } const newEngine = { name, searchUrl: url, searchkeyName: keys, matchUrl: new RegExp(`.*${new URL(url).hostname}.*`), mark, svgCode: "", custom: true }; if (iconValue) { if (iconType === "svg") { newEngine.svgCode = iconValue; } else if (iconType === "image") { newEngine.svgCode = `
`; } else if (iconType === "text") { newEngine.svgCode = ` ${iconValue} `; } else if (iconType === "emoji") { newEngine.svgCode = ` ${iconValue} `; } } userSearchEngines.push(newEngine); GM_setValue("userSearchEngines", userSearchEngines); searchUrlMap = [...defaultSearchEngines, ...userSearchEngines]; const currentSetup = GM_getValue("punk_setup_search", punkDeafultMark); GM_setValue("punk_setup_search", currentSetup + "-" + mark); // 标记有更改需要保存 markUnsavedChanges(); alert("✅ 搜索引擎添加成功!"); showAddForm(false); refreshEngineList(); } // 恢复默认设置 function resetToDefault() { if (confirm("⚠️ 确定要恢复默认设置吗?这将删除所有自定义搜索引擎。")) { userSearchEngines = []; GM_setValue("userSearchEngines", []); GM_setValue("punk_setup_search", punkDeafultMark); searchUrlMap = [...defaultSearchEngines]; // 标记有更改需要保存 markUnsavedChanges(); alert("✅ 已恢复默认设置"); refreshEngineList(); } } // 刷新引擎列表显示 function refreshEngineList() { const engineList = document.getElementById("engine-management-list"); const activeMarks = (GM_getValue("punk_setup_search", punkDeafultMark)).split("-"); engineList.innerHTML = ""; searchUrlMap.forEach((engine, index) => { const engineCard = document.createElement("div"); engineCard.className = "engine-card"; engineCard.style.cssText = ` display: flex; align-items: center; padding: 15px; background: white; border: 2px solid ${activeMarks.includes(engine.mark) ? '#27ae60' : '#ecf0f1'}; border-radius: 10px; transition: all 0.3s ease; cursor: grab; min-height: 60px; box-sizing: border-box; `; engineCard.addEventListener("mouseenter", () => { engineCard.style.boxShadow = "0 4px 12px rgba(0,0,0,0.1)"; engineCard.style.transform = "translateY(-2px)"; }); engineCard.addEventListener("mouseleave", () => { engineCard.style.boxShadow = "none"; engineCard.style.transform = "translateY(0)"; }); // 选择复选框 const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.dataset.mark = engine.mark; checkbox.checked = activeMarks.includes(engine.mark); checkbox.style.marginRight = "15px"; checkbox.style.transform = "scale(1.2)"; // 复选框更改时标记有未保存更改 checkbox.addEventListener("change", () => { updateSelectedCount(); markUnsavedChanges(); }); // 引擎图标预览 const iconPreview = document.createElement("div"); iconPreview.style.cssText = ` width: 40px; height: 25px; background-image: url('data:image/svg+xml;utf8,${encodeURIComponent(engine.svgCode)}'); background-size: contain; background-repeat: no-repeat; background-position: center; margin-right: 15px; border: 1px solid #eee; border-radius: 5px; flex-shrink: 0; `; // 引擎信息 const infoContainer = document.createElement("div"); infoContainer.style.flexGrow = "1"; infoContainer.style.minWidth = "0"; const name = document.createElement("div"); name.textContent = engine.name; name.style.fontWeight = "bold"; name.style.color = "#2c3e50"; name.style.marginBottom = "5px"; name.style.whiteSpace = "nowrap"; name.style.overflow = "hidden"; name.style.textOverflow = "ellipsis"; const url = document.createElement("div"); url.textContent = engine.searchUrl; url.style.fontSize = "0.8em"; url.style.color = "#7f8c8d"; url.style.whiteSpace = "nowrap"; url.style.overflow = "hidden"; url.style.textOverflow = "ellipsis"; infoContainer.appendChild(name); infoContainer.appendChild(url); // 操作按钮 const actions = document.createElement("div"); actions.style.display = "flex"; actions.style.gap = "5px"; actions.style.flexShrink = "0"; if (engine.custom) { const deleteBtn = document.createElement("button"); deleteBtn.textContent = "🗑️"; deleteBtn.title = "删除"; deleteBtn.style.cssText = ` padding: 5px 10px; border: none; background: #e74c3c; color: white; border-radius: 5px; cursor: pointer; flex-shrink: 0; `; actions.appendChild(deleteBtn); deleteBtn.addEventListener("click", (e) => { e.stopPropagation(); if (confirm(`确定要删除 ${engine.name} 吗?`)) { userSearchEngines = userSearchEngines.filter(e => e.mark !== engine.mark); GM_setValue("userSearchEngines", userSearchEngines); const currentSetup = GM_getValue("punk_setup_search", punkDeafultMark); const newSetup = currentSetup.split("-").filter(m => m !== engine.mark).join("-"); GM_setValue("punk_setup_search", newSetup); searchUrlMap = [...defaultSearchEngines, ...userSearchEngines]; // 标记有更改需要保存 markUnsavedChanges(); refreshEngineList(); } }); } engineCard.appendChild(checkbox); engineCard.appendChild(iconPreview); engineCard.appendChild(infoContainer); engineCard.appendChild(actions); engineList.appendChild(engineCard); }); updateSelectedCount(); } // 更新选中计数 function updateSelectedCount() { const checkboxes = document.querySelectorAll('#engine-management-list input[type="checkbox"]:checked'); const countElement = document.getElementById("selected-count"); countElement.textContent = `已选择 ${checkboxes.length} 个引擎`; } // 保存引擎设置 function saveEngineSettings() { const checkboxes = document.querySelectorAll('#engine-management-list input[type="checkbox"]'); const activeMarks = []; checkboxes.forEach(checkbox => { if (checkbox.checked) { activeMarks.push(checkbox.dataset.mark); } }); if (activeMarks.length === 0) { alert("⚠️ 请至少选择一个搜索引擎"); return; } GM_setValue("punk_setup_search", activeMarks.join("-")); // 清除未保存更改标记 clearUnsavedChanges(); // 延迟关闭面板,让用户看到成功提示 setTimeout(() => { document.getElementById("engine-management-panel").style.display = "none"; reloadScript(); }, 1000); } // 显示管理面板 function showManagementPanel() { const panel = document.getElementById("engine-management-panel") || createManagementPanel(); // 重置未保存更改状态 hasUnsavedChanges = false; clearUnsavedChanges(); refreshEngineList(); panel.style.display = "block"; } // 拖拽排序功能 function enableDragAndSort() { const container = document.querySelector('.engine-display'); if (!container) return; const buttons = container.querySelectorAll('.engine-button'); buttons.forEach(button => { button.draggable = true; button.addEventListener('dragstart', (e) => { currentDraggedButton = button; e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain', button.getAttribute('url')); button.classList.add('dragging'); }); button.addEventListener('dragend', () => { button.classList.remove('dragging'); currentDraggedButton = null; saveButtonOrder(); }); button.addEventListener('dragover', (e) => { e.preventDefault(); return false; }); button.addEventListener('dragenter', (e) => { e.preventDefault(); button.classList.add('drag-over'); }); button.addEventListener('dragleave', () => { button.classList.remove('drag-over'); }); button.addEventListener('drop', (e) => { e.preventDefault(); button.classList.remove('drag-over'); if (currentDraggedButton !== button) { const container = button.parentNode; const buttonsArray = Array.from(container.querySelectorAll('.engine-button')); const draggedIndex = buttonsArray.indexOf(currentDraggedButton); const targetIndex = buttonsArray.indexOf(button); if (draggedIndex < targetIndex) { container.insertBefore(currentDraggedButton, button.nextSibling); } else { container.insertBefore(currentDraggedButton, button); } // 拖拽排序后标记有未保存更改 markUnsavedChanges(); } return false; }); }); } // 保存按钮顺序 function saveButtonOrder() { const container = document.querySelector('.engine-display'); if (!container) return; const buttons = container.querySelectorAll('.engine-button'); const newOrder = Array.from(buttons).map(btn => { const url = btn.getAttribute('url'); const engine = searchUrlMap.find(e => e.searchUrl === url); return engine ? engine.mark : null; }).filter(mark => mark !== null).join('-'); GM_setValue('punk_setup_search', newOrder); } function addSearchBox() { try { if (engineContainerExists()) { console.log("引擎容器已存在,跳过添加"); return; } const punkJetBox = document.createElement("div"); punkJetBox.id = "punkjet-search-box"; punkJetBox.className = "engine-container"; punkJetBox.style.display = 'flex'; punkJetBox.style.zIndex = '9999'; punkJetBox.style.position = 'fixed'; updateSearchBoxPosition(); const ulList = document.createElement('div'); ulList.className = "engine-display"; // 添加设置按钮 const settingsButton = document.createElement('button'); settingsButton.className = "engine-settings-button"; settingsButton.innerHTML = "ν"; settingsButton.title = "管理搜索引擎"; settingsButton.style.width = "32px"; settingsButton.style.height = "32px"; settingsButton.style.border = "1px solid #f0f0f0"; settingsButton.style.borderRadius = "7px"; settingsButton.style.background = "white"; settingsButton.style.cursor = "pointer"; settingsButton.style.margin = "3px"; settingsButton.style.flexShrink = "0"; settingsButton.style.display = "flex"; settingsButton.style.justifyContent = "center"; settingsButton.style.alignItems = "center"; settingsButton.style.fontSize = "18px"; settingsButton.addEventListener('click', showManagementPanel); punkJetBox.appendChild(settingsButton); let fragment = document.createDocumentFragment(); let showList = GM_getValue("punk_setup_search", punkDeafultMark); showList = showList.split('-'); for (let showListIndex = 0; showListIndex < showList.length; showListIndex++) { for (let index = 0; index < searchUrlMap.length; index++) { let item = searchUrlMap[index]; if (item.mark === showList[showListIndex]) { let button = document.createElement('button'); button.className = "engine-button"; button.style.backgroundImage = `url('data:image/svg+xml;utf8,${encodeURIComponent(item.svgCode)}')`; button.setAttribute("url", item.searchUrl); button.setAttribute("title", item.name); button.innerHTML = ''; fragment.appendChild(button); button.addEventListener('mouseover', () => { button.style.backgroundColor = 'rgba(241, 241, 241, 1)'; button.style.transform = 'translateY(-2px)'; button.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)'; }); button.addEventListener('mouseout', () => { button.style.backgroundColor = 'rgba(240, 240, 244, 1)'; button.style.transform = 'translateY(0)'; button.style.boxShadow = '1px 1px 1px rgba(0, 0, 0, 0.1), 0px 0px 0px rgba(255, 255, 255, 0.5), 6px 6px 10px rgba(0, 0, 0, 0.1) inset, -6px -6px 10px rgba(255, 255, 255, 0) inset'; }); button.addEventListener('click', (event) => { event.preventDefault(); const url = button.getAttribute("url"); let keywords = ""; const activeInput = document.activeElement; if (activeInput && (activeInput.tagName === 'INPUT' || activeInput.tagName === 'TEXTAREA')) { keywords = activeInput.value.trim(); } if (!keywords) { const baiduInput = document.querySelector('input#kw'); if (baiduInput) { keywords = baiduInput.value.trim(); } } if (!keywords) { keywords = getKeywords().trim(); } if (url && keywords) { const finalUrl = url.replace('{keyword}', encodeURIComponent(keywords)); window.open(finalUrl, '_blank'); } else { alert('未找到搜索关键词。'); } }); // 长按事件(打开上下文菜单) button.addEventListener('mousedown', (e) => { longPressTimer = setTimeout(() => { showContextMenu(e, item); }, 800); }); button.addEventListener('mouseup', () => { clearTimeout(longPressTimer); }); button.addEventListener('mouseleave', () => { clearTimeout(longPressTimer); }); break; } } } ulList.appendChild(fragment); punkJetBox.appendChild(ulList); document.body.appendChild(punkJetBox); containerAdded = true; console.log("引擎容器添加成功"); window.addEventListener('resize', updateSearchBoxPosition); setTimeout(enableDragAndSort, 500); } catch (error) { console.error("添加搜索框时出错:", error.message); } } // 显示上下文菜单 function showContextMenu(event, engine) { const menu = document.createElement("div"); menu.id = "engine-context-menu"; menu.style.cssText = ` position: fixed; left: ${event.pageX}px; top: ${event.pageY}px; background: white; border: 1px solid #ddd; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 10001; min-width: 120px; `; const hideOption = document.createElement("div"); hideOption.className = "context-menu-item"; hideOption.textContent = "👁️ 隐藏引擎"; hideOption.style.cssText = ` padding: 10px 15px; cursor: pointer; border-bottom: 1px solid #f0f0f0; transition: background 0.3s; `; hideOption.addEventListener("mouseenter", () => { hideOption.style.background = "#f8f9fa"; }); hideOption.addEventListener("mouseleave", () => { hideOption.style.background = "white"; }); hideOption.addEventListener("click", () => { const currentSetup = GM_getValue("punk_setup_search", punkDeafultMark); const newSetup = currentSetup.split("-").filter(m => m !== engine.mark).join("-"); GM_setValue("punk_setup_search", newSetup); reloadScript(); menu.remove(); }); menu.appendChild(hideOption); if (engine.custom) { const deleteOption = document.createElement("div"); deleteOption.className = "context-menu-item"; deleteOption.textContent = "🗑️ 删除引擎"; deleteOption.style.cssText = ` padding: 10px 15px; cursor: pointer; color: #e74c3c; transition: background 0.3s; `; deleteOption.addEventListener("mouseenter", () => { deleteOption.style.background = "#f8f9fa"; }); deleteOption.addEventListener("mouseleave", () => { deleteOption.style.background = "white"; }); deleteOption.addEventListener("click", () => { if (confirm(`确定要删除 ${engine.name} 吗?`)) { userSearchEngines = userSearchEngines.filter(e => e.mark !== engine.mark); GM_setValue("userSearchEngines", userSearchEngines); const currentSetup = GM_getValue("punk_setup_search", punkDeafultMark); const newSetup = currentSetup.split("-").filter(m => m !== engine.mark).join("-"); GM_setValue("punk_setup_search", newSetup); searchUrlMap = [...defaultSearchEngines, ...userSearchEngines]; reloadScript(); } menu.remove(); }); menu.appendChild(deleteOption); } document.body.appendChild(menu); const closeMenu = (e) => { if (!menu.contains(e.target)) { menu.remove(); document.removeEventListener("click", closeMenu); } }; setTimeout(() => { document.addEventListener("click", closeMenu); }, 100); } function injectStyle() { try { if (document.querySelector('style#engine-container-style')) { return; } const cssNode = document.createElement("style"); cssNode.id = "engine-container-style"; cssNode.textContent = ` .engine-container { display: flex; position: fixed; bottom: 0px; left: 2%; width: 96%; height: 36px; overflow: hidden; justify-content: center; align-items: center; z-index: 1000; background-color: rgba(255, 255, 255, 0); margin-top: 1px; } .engine-display { display: flex; overflow-x: auto; white-space: nowrap; height: 100%; gap: 0px; flex-grow: 1; scrollbar-width: thin; scrollbar-color: #bdc3c7 transparent; } .engine-display::-webkit-scrollbar { height: 4px; } .engine-display::-webkit-scrollbar-track { background: transparent; } .engine-display::-webkit-scrollbar-thumb { background: #bdc3c7; border-radius: 2px; } .engine-button { width: 55.5px; height: 32px; padding: 0; border: 1px solid #f0f0f0; border-radius: 8px; background-color: rgba(255, 255, 255, 1); color: transparent; font-size: 14px; cursor: pointer; margin: 2px; background-size: contain; background-repeat: no-repeat; background-position: center; backdrop-filter: blur(5px); box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1), 0px 0px 0px rgba(255, 255, 255, 0.5), 6px 6px 10px rgba(0, 0, 0, 0.1) inset, -6px -6px 10px rgba(255, 255, 255, 0) inset; transition: all 0.3s ease; flex-shrink: 0; } .engine-button:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.15); } .engine-button:focus { outline: none; border-color: #ffb61e; } .engine-button.selected { border: 1px solid #ffb61e; color: transparent; } .engine-button.dragging { opacity: 0.5; transform: rotate(5deg); } .engine-button.drag-over { border: 2px dashed #2196F3; background-color: #f0f8ff; } .context-menu-item:hover { background-color: #f8f9fa; } .engine-card { transition: all 0.3s ease; } .engine-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.1); transform: translateY(-2px); } #engine-management-panel { animation: slideIn 0.3s ease; } #panel-save-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.2); } @keyframes slideIn { from { opacity: 0; transform: translate(-50%, -48%); } to { opacity: 1; transform: translate(-50%, -50%); } } `; document.head.appendChild(cssNode); } catch (error) { console.error("注入样式时出错:", error.message); } } function reloadScript() { const punkJetBox = document.getElementById("punkjet-search-box"); if (punkJetBox) { punkJetBox.remove(); containerAdded = false; console.log("移除现有引擎容器"); } scriptLoaded = false; init(); } setInterval(() => { if (isValidScope() && !containerAdded) { init(); } else if (!isValidScope() && containerAdded) { reloadScript(); } }, 1000); function init() { try { if (containerAdded || scriptLoaded) { console.log("脚本已初始化或容器已存在,跳过"); return; } if (getKeywords() != null) { if (!scriptLoaded) { if (!GM_getValue("punk_setup_search")) { GM_setValue("punk_setup_search", punkDeafultMark); } currentInput = sessionStorage.getItem("currentInput") || ""; monitorInputFields(); addSearchBox(); injectStyle(); scriptLoaded = true; console.log("脚本初始化完成"); } } } catch (error) { console.error("初始化时出错:", error.message); } } document.addEventListener("visibilitychange", () => { if (document.visibilityState === 'visible') { if (!containerAdded) { init(); } } }); document.addEventListener("pageshow", (event) => { if (event.persisted) { if (!containerAdded) { init(); } } }); document.addEventListener("DOMContentLoaded", () => { if (isValidScope()) { init(); } });