Google Site Filter with Customizable Multi-select (OR Version)

在 Google 搜索工具栏右侧添加多选下拉菜单,并允许用户通过油猴菜单自定义需要筛选的站点,查询条件中多个 site: 之间用 OR 连接。支持深色模式。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Google Site Filter with Customizable Multi-select (OR Version)
// @namespace    http://tampermonkey.net/
// @version      1.2.0
// @description  在 Google 搜索工具栏右侧添加多选下拉菜单,并允许用户通过油猴菜单自定义需要筛选的站点,查询条件中多个 site: 之间用 OR 连接。支持深色模式。
// @match        https://www.google.com/search*
// @run-at       document-end
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  // 默认的站点列表(如果用户没有自定义,则使用此列表)
  const defaultSites = ["site1.com", "site2.com", "site3.com"];
  // 读取用户存储的站点列表,未存储时使用默认值
  let sites = GM_getValue("sites", defaultSites);

  // 注册油猴菜单命令,允许用户自定义站点列表
  GM_registerMenuCommand('设置站点筛选', function () {
    const currentSites = Array.isArray(sites) ? sites.join(', ') : sites;
    const input = prompt('请输入需要筛选的站点列表(以逗号分隔):', currentSites);
    if (input !== null) {
      sites = input.split(',').map(s => s.trim()).filter(Boolean);
      GM_setValue("sites", sites);
      alert("站点筛选设置已更新,请刷新页面以应用新设置!");
    }
  });

  // 元素引用
  let customBtn, dropdown, applyBtn;

  // 检测深色模式
  function isDarkMode() {
    // 使用 prefers-color-scheme 检测深色模式
    return window.matchMedia('(prefers-color-scheme: dark)').matches;
  }

  // 更新样式以适应深色模式
  function updateStyles() {
    const darkMode = isDarkMode();
    if (customBtn) {
      customBtn.style.background = darkMode ? '#303134' : '#fff';
      customBtn.style.color = darkMode ? '#e8eaed' : 'inherit';
      customBtn.style.borderColor = darkMode ? '#5f6368' : '#ccc';
    }
    if (dropdown) {
      dropdown.style.background = darkMode ? '#303134' : '#fff';
      dropdown.style.color = darkMode ? '#e8eaed' : 'inherit';
      dropdown.style.borderColor = darkMode ? '#5f6368' : '#ccc';
    }
    if (applyBtn) {
      applyBtn.style.background = darkMode ? '#8ab4f8' : '#4285f4';
      applyBtn.style.color = darkMode ? '#202124' : '#fff';
    }
    if (dropdown) {
      dropdown.querySelectorAll('label').forEach(label => {
        label.style.color = darkMode ? '#e8eaed' : 'inherit';
      });
    }
  }

  // 监听深色模式变化
  window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
    updateStyles();
  });

  // 添加下拉菜单
  function addSiteFilterDropdown() {
    const toolbar = document.getElementById("hdtb");
    if (!toolbar) {
      setTimeout(addSiteFilterDropdown, 500);
      return;
    }

    // 创建过滤按钮
    customBtn = document.createElement("div");
    customBtn.innerHTML = '站点筛选 ▼';
    Object.assign(customBtn.style, {
      position: 'absolute',
      right: '10px',
      top: '50%',
      transform: 'translateY(-50%)',
      padding: '4px 8px',
      borderRadius: '4px',
      cursor: 'pointer',
      userSelect: 'none',
      fontSize: '13px',
      zIndex: '1000'
    });

    // 创建下拉容器
    dropdown = document.createElement("div");
    Object.assign(dropdown.style, {
      position: 'absolute',
      top: '110%',
      right: '0',
      boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
      padding: '10px',
      zIndex: '1001',
      display: 'none',
      minWidth: '150px',
      borderRadius: '4px'
    });

    // 动态添加站点选项
    sites.forEach(site => {
      const label = document.createElement("label");
      label.style.display = 'flex';
      label.style.alignItems = 'center';
      label.style.marginBottom = '8px';
      const checkbox = document.createElement("input");
      checkbox.type = 'checkbox';
      checkbox.value = `site:${site}`;
      checkbox.style.marginRight = '8px';
      label.append(checkbox, document.createTextNode(site));
      dropdown.appendChild(label);
    });

    // 创建应用按钮
    applyBtn = document.createElement("button");
    applyBtn.innerHTML = '应用筛选';
    Object.assign(applyBtn.style, {
      width: '100%',
      padding: '8px',
      marginTop: '8px',
      borderRadius: '4px',
      border: 'none',
      cursor: 'pointer',
      fontSize: '13px'
    });

    // 应用筛选逻辑
    applyBtn.addEventListener('click', (e) => {
      e.stopPropagation();
      const queryInput = document.querySelector('input[name="q"]');
      if (!queryInput) return;
      const selected = Array.from(dropdown.querySelectorAll('input:checked'))
        .map(c => c.value).join(' OR ');
      if (selected) {
        const newQuery = `${queryInput.value} (${selected})`;
        window.location.href = `https://www.google.com/search?q=${encodeURIComponent(newQuery)}`;
      }
    });

    dropdown.appendChild(applyBtn);
    // 阻止下拉菜单的点击事件冒泡
    dropdown.addEventListener("click", (e) => e.stopPropagation());
    customBtn.appendChild(dropdown);
    toolbar.appendChild(customBtn);

    // 初始样式设置
    updateStyles();

    // 交互逻辑
    customBtn.addEventListener('click', (e) => {
      e.stopPropagation();
      dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none';
    });
    document.addEventListener('click', () => (dropdown.style.display = 'none'));
  }

  // 页面加载完成后初始化
  window.addEventListener('load', addSiteFilterDropdown);
})();