// ==UserScript== // @name 推特搜索助手-Twitter Search Assistant Enhanced // @namespace example.twitter.enhanced // @version 2.22 // @description 推特搜索助手 // @match https://twitter.com/* // @match https://x.com/* // @grant none // @license MIT // @downloadURL none // ==/UserScript== (function () { 'use strict'; // 定义搜索预设 const presets = { "📷 图片": "filter:images -filter:retweets -filter:replies", "🎬 视频": "filter:videos -filter:retweets -filter:replies", "🔥 高热度": "min_faves:200 -filter:retweets", "🈶 日语": "lang:ja -filter:retweets -filter:replies", "🌎 英语": "lang:en -filter:retweets -filter:replies", "⏰ 近期": "within_time:180d -filter:retweets", // 最近半年(180天) }; // 历史记录管理 const MAX_HISTORY = 20; // 主面板HTML - 增加历史面板 const container = document.createElement('div'); container.id = 'tw-search-container'; container.innerHTML = `
搜索助手
单选模式
搜索历史
`; // 样式 - 修改间距、宽度和位置 const style = document.createElement('style'); style.textContent = ` #tw-search-container { position: fixed; top: 5px; right: 70px; /* 搜索框整体偏移距离,数值越小越靠右 */ display: flex; gap: 4px; /* 紧贴间距从8px减少到4px */ z-index: 10000; align-items: stretch; width: auto; /* 容器宽度自动 */ min-width: 0; /* 防止子元素撑开 */ } #tw-search-assistant, #tw-history-panel { background: #ffffff; border: 1px solid #e1e8ed; border-radius: 12px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 13px; color: #0f1419; transition: all 0.3s ease; opacity: 0; transform: translateY(-10px); box-sizing: border-box; } #tw-search-assistant { width: 280px; /* 替换flex为固定宽度 */ min-width: 0; /* 防止内容撑开 */ flex-shrink: 0; /* 防止收缩 */ } #tw-history-panel { width: 160px; /* 替换flex为固定宽度 */ min-width: 0; /* 防止内容撑开 */ max-width: 160px; /* 最大宽度限制 */ flex-shrink: 0; /* 防止收缩 */ overflow: hidden; /* 防止内容溢出 */ } #tw-search-assistant.show, #tw-history-panel.show { opacity: 1; transform: translateY(0); } #tw-search-assistant.hidden, #tw-history-panel.hidden { opacity: 0 !important; pointer-events: none !important; z-index: -1 !important; } #tw-search-assistant:hover, #tw-history-panel:hover { box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12); } .panel-header, .history-header { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px 8px; border-bottom: 1px solid #eff3f4; font-weight: 600; width: 100%; /* 填满父容器 */ box-sizing: border-box; /* 包含padding */ overflow: hidden; /* 防止溢出 */ } .history-header { padding: 10px 12px 6px; /* 历史栏标题宽度 */ } .mode-indicator.clickable { padding: 6px 16px; font-size: 12px; color: #536471; background: #f7f9fa; margin: 0 16px 8px; border-radius: 6px; text-align: center; cursor: pointer; transition: all 0.2s; user-select: none; } .mode-indicator.clickable:hover { background: #e1e8ed; } .mode-indicator.multi-mode { background: #e8f5fe; color: #1da1f2; } .mode-indicator.multi-mode:hover { background: #d0e9f9; } .clear-history { background: none; border: none; cursor: pointer; font-size: 14px; padding: 2px 4px; border-radius: 4px; transition: background 0.2s; flex-shrink: 0; /* 防止被压缩 */ } .clear-history:hover { background: #f7f9fa; } .keyword-container { padding: 0 16px 12px; width: 100%; box-sizing: border-box; } #tw-keyword { width: 100%; padding: 8px 12px; border: 1px solid #eff3f4; border-radius: 8px; font-size: 14px; outline: none; transition: border-color 0.2s; box-sizing: border-box; } #tw-keyword:focus { border-color: #1da1f2; box-shadow: 0 0 0 3px rgba(29, 161, 242, 0.1); } .preset-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; padding: 0 16px 12px; width: 100%; box-sizing: border-box; } .preset-btn { padding: 8px 12px; background: #f7f9fa; border: 1px solid #eff3f4; border-radius: 8px; color: #0f1419; cursor: pointer; font-size: 12px; transition: all 0.2s; display: flex; align-items: center; gap: 4px; box-sizing: border-box; overflow: hidden; /* 防止溢出 */ white-space: nowrap; /* 文本不换行 */ text-overflow: ellipsis; /* 截断显示省略号 */ } .preset-btn:hover { background: #e8f5fe; border-color: #cfe5f7; } .preset-btn.selected { background: #e8f5fe; color: #1da1f2; border-color: #1da1f2; } .preset-btn.selected::after { content: '✓'; margin-left: auto; font-size: 11px; font-weight: bold; } .history-list { padding: 4px 8px; /* 历史栏内边距 */ max-height: 400px; overflow-y: auto; width: 100%; /* 填满父容器 */ box-sizing: border-box; /* 包含padding */ overflow-x: hidden; /* 防止水平滚动 */ } .history-item { padding: 6px 8px; /* 历史项内边距 */ border-radius: 6px; cursor: pointer; transition: background 0.2s; font-size: 12px; color: #0f1419; margin-bottom: 2px; /* 历史项间距 */ white-space: nowrap; /* 强制不换行 */ overflow: hidden; /* 隐藏溢出 */ text-overflow: ellipsis; /* 显示省略号 */ width: 100%; /* 填满容器 */ max-width: 100%; /* 最大宽度限制 */ box-sizing: border-box; /* 包含padding */ display: block; /* 块级元素 */ } .history-item:hover { background: #f7f9fa; } .history-item:active { background: #e1e8ed; } .empty-history { padding: 16px 8px; /* 空状态提示大小 */ text-align: center; color: #536471; font-size: 11px; /* 字体大小 */ width: 100%; box-sizing: border-box; } .action-buttons { display: flex; gap: 8px; padding: 0 16px 16px; width: 100%; box-sizing: border-box; } .btn-clear, .btn-apply { flex: 1; padding: 8px; border: none; border-radius: 8px; font-size: 13px; cursor: pointer; transition: all 0.2s; font-weight: 500; box-sizing: border-box; overflow: hidden; /* 防止溢出 */ white-space: nowrap; text-overflow: ellipsis; } .btn-clear { background: #f7f9fa; color: #536471; border: 1px solid #eff3f4; } .btn-clear:hover { background: #e1e8ed; } .btn-apply { background: #1da1f2; color: white; } .btn-apply:hover { background: #1a91da; } .btn-apply.active { background: #17bf63; box-shadow: 0 2px 8px rgba(23, 191, 99, 0.3); } `; document.head.appendChild(style); document.body.appendChild(container); // 状态管理 let isMultiSelectMode = false; let selectedPresets = new Set(); // 历史记录功能 function getHistory() { try { const history = localStorage.getItem('tw-search-history'); return history ? JSON.parse(history) : []; } catch (e) { return []; } } function saveHistory(keyword) { if (!keyword || keyword.trim() === '') return; keyword = keyword.trim(); let history = getHistory(); // 移除重复项 history = history.filter(item => item !== keyword); // 添加到最前面 history.unshift(keyword); // 限制数量 if (history.length > MAX_HISTORY) { history = history.slice(0, MAX_HISTORY); } try { localStorage.setItem('tw-search-history', JSON.stringify(history)); } catch (e) { // 存储失败时的静默处理 } renderHistory(); } function clearAllHistory() { try { localStorage.removeItem('tw-search-history'); renderHistory(); } catch (e) { // 清空失败时的静默处理 } } function renderHistory() { const historyList = document.querySelector('.history-list'); const history = getHistory(); if (history.length === 0) { historyList.innerHTML = '
暂无搜索历史
'; return; } historyList.innerHTML = history.map(item => `
${escapeHtml(item)}
` ).join(''); // 绑定点击事件 historyList.querySelectorAll('.history-item').forEach(item => { item.addEventListener('click', () => { const keyword = decodeURIComponent(item.getAttribute('data-keyword')); document.getElementById('tw-keyword').value = keyword; // 不触发搜索,只是填充 }); }); } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // 极速消失检测 - 监听特定元素出现 function initMediaDetection() { const observer = new MutationObserver(() => { const modal = document.querySelector('[aria-modal="true"]') || document.querySelector('[data-testid="swipe-to-dismiss-container"]') || document.querySelector('[data-testid="media-modal"]'); const assistant = document.getElementById('tw-search-assistant'); const history = document.getElementById('tw-history-panel'); if (modal) { assistant.classList.add('hidden'); history.classList.add('hidden'); } else { assistant.classList.remove('hidden'); history.classList.remove('hidden'); } }); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['aria-modal', 'style'] }); } // 简化:只提取关键词,不要任何过滤条件 function extractKeywordOnly(url) { if (!url.includes('/search?q=')) return ''; try { const match = url.match(/\/search\?q=([^&]+)/); if (!match) return ''; const query = decodeURIComponent(match[1]); const keyword = query.split(/\s+(?:filter:|lang:|min_faves:|since:|from:|to:|until:|OR|AND|NOT)/)[0].trim(); return keyword; } catch (e) { return ''; } } // 处理预设点击 function handlePresetClick(btn, filter) { if (isMultiSelectMode) { if (selectedPresets.has(filter)) { selectedPresets.delete(filter); btn.classList.remove('selected'); } else { selectedPresets.add(filter); btn.classList.add('selected'); } updateApplyButton(); } else { const keyword = document.getElementById('tw-keyword').value.trim() || extractKeywordOnly(window.location.href); if (!keyword) { alert('请输入关键词'); return; } saveHistory(keyword); const searchUrl = `https://twitter.com/search?q=${encodeURIComponent(keyword + ' ' + filter)}&src=typed_query&f=top`; window.location.href = searchUrl; } } // 更新应用按钮 function updateApplyButton() { const applyBtn = document.querySelector('.btn-apply'); if (isMultiSelectMode && selectedPresets.size > 0) { applyBtn.classList.add('active'); applyBtn.textContent = `应用搜索(${selectedPresets.size})`; } else if (isMultiSelectMode) { applyBtn.classList.remove('active'); applyBtn.textContent = '应用搜索'; } } // 多选模式搜索 - 彻底清空旧条件 function applyMultiSelect() { if (!isMultiSelectMode || selectedPresets.size === 0) { alert('多选模式下请至少选择一个筛选条件'); return; } const keyword = document.getElementById('tw-keyword').value.trim() || extractKeywordOnly(window.location.href); if (!keyword) { alert('请输入关键词'); return; } saveHistory(keyword); const selectedFilters = Array.from(selectedPresets).join(' '); const finalQuery = keyword + ' ' + selectedFilters; const searchUrl = `https://twitter.com/search?q=${encodeURIComponent(finalQuery.trim())}&src=typed_query&f=top`; window.location.href = searchUrl; } // 清空选择 function clearSelection() { selectedPresets.clear(); document.querySelectorAll('.preset-btn.selected').forEach(btn => { btn.classList.remove('selected'); }); updateApplyButton(); } // 切换模式 function toggleMode() { isMultiSelectMode = !isMultiSelectMode; const indicator = document.querySelector('.mode-indicator'); const applyBtn = document.querySelector('.btn-apply'); if (isMultiSelectMode) { indicator.textContent = '多选模式'; indicator.classList.add('multi-mode'); applyBtn.style.display = 'block'; clearSelection(); } else { indicator.textContent = '单选模式'; indicator.classList.remove('multi-mode'); applyBtn.style.display = 'none'; clearSelection(); } } // 自动填充关键词 function autoFillKeyword() { const keyword = extractKeywordOnly(window.location.href); if (keyword && !document.getElementById('tw-keyword').value) { document.getElementById('tw-keyword').value = keyword; } } // 初始化按钮 function initButtons() { const grid = document.querySelector('.preset-grid'); Object.keys(presets).forEach(name => { const btn = document.createElement('button'); btn.className = 'preset-btn'; btn.textContent = name; btn.onclick = () => handlePresetClick(btn, presets[name]); grid.appendChild(btn); }); } // 监听URL变化 observeUrlChanges(); function observeUrlChanges() { let currentUrl = window.location.href; setInterval(() => { if (window.location.href !== currentUrl) { currentUrl = window.location.href; autoFillKeyword(); clearSelection(); } }, 500); } // 绑定事件 document.querySelector('.mode-indicator').onclick = toggleMode; // 直接点击文字切换模式 document.querySelector('.btn-clear').onclick = clearSelection; document.querySelector('.btn-apply').onclick = applyMultiSelect; document.querySelector('.clear-history').onclick = clearAllHistory; // 初始化 initButtons(); autoFillKeyword(); renderHistory(); initMediaDetection(); // 显示面板 setTimeout(() => { document.getElementById('tw-search-assistant').classList.add('show'); document.getElementById('tw-history-panel').classList.add('show'); }, 100); })();