Google SEO Filter Sidebar

Adds a powerful SEO filter sidebar to Google Search with filetype, date range, site filter, keyword location, and more.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

ستحتاج إلى تثبيت إضافة مثل Stylus لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتتمكن من تثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

(لدي بالفعل مثبت أنماط للمستخدم، دعني أقم بتثبيته!)

// ==UserScript==
// @name         Google SEO Filter Sidebar
// @namespace    https://github.com/seo-sidebar
// @version      1.0.0
// @description  Adds a powerful SEO filter sidebar to Google Search with filetype, date range, site filter, keyword location, and more.
// @author       SEO Sidebar
// @match        https://www.google.com/search*
// @match        https://google.com/search*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function () {
  'use strict';

  // ─── Prevent double injection ───────────────────────────────────────────────
  if (document.getElementById('seo-sidebar-root')) return;

  // ─── Styles ─────────────────────────────────────────────────────────────────
  const style = document.createElement('style');
  style.textContent = `
    @import url('https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=DM+Sans:wght@400;500;600&display=swap');

    #seo-sidebar-toggle {
      position: fixed;
      top: 50%;
      right: 0;
      transform: translateY(-50%);
      z-index: 99999;
      background: #1a1a2e;
      color: #e0e0ff;
      border: none;
      border-radius: 8px 0 0 8px;
      padding: 10px 6px;
      cursor: pointer;
      font-size: 18px;
      writing-mode: vertical-rl;
      letter-spacing: 2px;
      font-family: 'DM Mono', monospace;
      font-size: 11px;
      font-weight: 500;
      text-transform: uppercase;
      gap: 6px;
      display: flex;
      align-items: center;
      box-shadow: -2px 0 12px rgba(0,0,0,0.3);
      transition: background 0.2s, right 0.3s ease;
    }

    #seo-sidebar-toggle:hover {
      background: #16213e;
    }

    #seo-sidebar-toggle .toggle-arrow {
      writing-mode: horizontal-tb;
      font-size: 14px;
      margin-bottom: 6px;
    }

    #seo-sidebar-root {
      position: fixed;
      top: 0;
      right: -340px;
      width: 320px;
      height: 100vh;
      z-index: 99998;
      background: #0f0f1a;
      color: #e0e0ff;
      font-family: 'DM Sans', sans-serif;
      display: flex;
      flex-direction: column;
      box-shadow: -4px 0 30px rgba(0,0,0,0.5);
      transition: right 0.3s cubic-bezier(0.4, 0, 0.2, 1);
      border-left: 1px solid rgba(255,255,255,0.07);
    }

    #seo-sidebar-root.open {
      right: 0;
    }

    #seo-sidebar-root.open ~ #seo-sidebar-toggle {
      right: 320px;
    }

    .seo-header {
      padding: 18px 20px 14px;
      border-bottom: 1px solid rgba(255,255,255,0.08);
      display: flex;
      align-items: center;
      justify-content: space-between;
    }

    .seo-header h2 {
      font-family: 'DM Mono', monospace;
      font-size: 12px;
      font-weight: 500;
      letter-spacing: 2px;
      text-transform: uppercase;
      color: #7c7cff;
      margin: 0;
    }

    .seo-header span {
      font-size: 10px;
      color: rgba(224,224,255,0.35);
      font-family: 'DM Mono', monospace;
    }

    .seo-body {
      flex: 1;
      overflow-y: auto;
      padding: 16px 20px;
      display: flex;
      flex-direction: column;
      gap: 20px;
    }

    .seo-body::-webkit-scrollbar { width: 4px; }
    .seo-body::-webkit-scrollbar-track { background: transparent; }
    .seo-body::-webkit-scrollbar-thumb { background: rgba(124,124,255,0.3); border-radius: 2px; }

    .seo-section {
      display: flex;
      flex-direction: column;
      gap: 8px;
    }

    .seo-section-label {
      font-family: 'DM Mono', monospace;
      font-size: 10px;
      font-weight: 500;
      letter-spacing: 1.5px;
      text-transform: uppercase;
      color: rgba(255,255,255,0.3);
      display: flex;
      align-items: center;
      gap: 6px;
    }

    .seo-section-label::after {
      content: '';
      flex: 1;
      height: 1px;
      background: rgba(255,255,255,0.07);
    }

    .seo-input, .seo-select {
      width: 100%;
      background: rgba(255,255,255,0.05);
      border: 1px solid rgba(255,255,255,0.1);
      border-radius: 7px;
      color: #e0e0ff;
      font-family: 'DM Sans', sans-serif;
      font-size: 13px;
      padding: 9px 12px;
      outline: none;
      box-sizing: border-box;
      transition: border-color 0.2s, background 0.2s;
      appearance: none;
    }

    .seo-input:focus, .seo-select:focus {
      border-color: #7c7cff;
      background: rgba(124,124,255,0.08);
    }

    .seo-input::placeholder {
      color: rgba(224,224,255,0.25);
    }

    .seo-select {
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%237c7cff' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
      background-repeat: no-repeat;
      background-position: right 12px center;
      padding-right: 32px;
      cursor: pointer;
    }

    .seo-select option {
      background: #1a1a2e;
      color: #e0e0ff;
    }

    .seo-date-row {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 8px;
    }

    .seo-date-wrap {
      display: flex;
      flex-direction: column;
      gap: 4px;
    }

    .seo-date-wrap small {
      font-size: 10px;
      color: rgba(224,224,255,0.3);
      font-family: 'DM Mono', monospace;
      padding-left: 2px;
    }

    input[type="date"].seo-input::-webkit-calendar-picker-indicator {
      filter: invert(0.7) sepia(1) saturate(3) hue-rotate(200deg);
      cursor: pointer;
    }

    .seo-chip-row {
      display: flex;
      flex-wrap: wrap;
      gap: 6px;
    }

    .seo-chip {
      background: rgba(255,255,255,0.05);
      border: 1px solid rgba(255,255,255,0.1);
      border-radius: 20px;
      padding: 5px 11px;
      font-size: 12px;
      cursor: pointer;
      color: rgba(224,224,255,0.6);
      font-family: 'DM Mono', monospace;
      transition: all 0.15s;
      user-select: none;
    }

    .seo-chip:hover {
      border-color: #7c7cff;
      color: #7c7cff;
    }

    .seo-chip.active {
      background: rgba(124,124,255,0.15);
      border-color: #7c7cff;
      color: #a0a0ff;
    }

    .seo-footer {
      padding: 16px 20px;
      border-top: 1px solid rgba(255,255,255,0.08);
      display: flex;
      flex-direction: column;
      gap: 8px;
    }

    .seo-btn {
      width: 100%;
      padding: 11px;
      border: none;
      border-radius: 8px;
      font-family: 'DM Sans', sans-serif;
      font-size: 13px;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.2s;
    }

    .seo-btn-primary {
      background: #7c7cff;
      color: #fff;
    }

    .seo-btn-primary:hover {
      background: #9090ff;
      transform: translateY(-1px);
      box-shadow: 0 4px 16px rgba(124,124,255,0.35);
    }

    .seo-btn-secondary {
      background: rgba(255,255,255,0.05);
      color: rgba(224,224,255,0.5);
      border: 1px solid rgba(255,255,255,0.08);
    }

    .seo-btn-secondary:hover {
      background: rgba(255,255,255,0.09);
      color: rgba(224,224,255,0.8);
    }

    .seo-query-preview {
      font-family: 'DM Mono', monospace;
      font-size: 11px;
      color: rgba(124,124,255,0.6);
      background: rgba(124,124,255,0.06);
      border: 1px solid rgba(124,124,255,0.15);
      border-radius: 6px;
      padding: 8px 10px;
      word-break: break-all;
      min-height: 36px;
      line-height: 1.6;
    }

    .seo-query-preview-label {
      font-size: 9px;
      letter-spacing: 1.5px;
      text-transform: uppercase;
      color: rgba(255,255,255,0.2);
      font-family: 'DM Mono', monospace;
      margin-bottom: 4px;
    }

    .seo-toast {
      position: fixed;
      bottom: 24px;
      right: 24px;
      background: #1a1a2e;
      color: #a0a0ff;
      border: 1px solid rgba(124,124,255,0.3);
      border-radius: 8px;
      padding: 10px 16px;
      font-family: 'DM Mono', monospace;
      font-size: 12px;
      z-index: 999999;
      opacity: 0;
      transform: translateY(8px);
      transition: all 0.25s;
      pointer-events: none;
    }
    .seo-toast.show {
      opacity: 1;
      transform: translateY(0);
    }
  `;
  document.head.appendChild(style);

  // ─── Sidebar HTML ────────────────────────────────────────────────────────────
  const sidebar = document.createElement('div');
  sidebar.id = 'seo-sidebar-root';
  sidebar.innerHTML = `
    <div class="seo-header">
      <h2>⬡ SEO Filters</h2>
      <span>google operator builder</span>
    </div>
    <div class="seo-body">

      <div class="seo-section">
        <div class="seo-section-label">Keyword</div>
        <input class="seo-input" id="seo-keyword" type="text" placeholder='e.g. "best laptops 2024"' />
      </div>

      <div class="seo-section">
        <div class="seo-section-label">Keyword Location</div>
        <div class="seo-chip-row" id="seo-kw-location">
          <span class="seo-chip" data-val="intitle">intitle</span>
          <span class="seo-chip" data-val="inurl">inurl</span>
          <span class="seo-chip" data-val="intext">intext</span>
          <span class="seo-chip" data-val="inanchor">inanchor</span>
          <span class="seo-chip" data-val="allintitle">allintitle</span>
          <span class="seo-chip" data-val="allinurl">allinurl</span>
        </div>
      </div>

      <div class="seo-section">
        <div class="seo-section-label">Site / Domain</div>
        <input class="seo-input" id="seo-site" type="text" placeholder="e.g. reddit.com or .gov" />
      </div>

      <div class="seo-section">
        <div class="seo-section-label">Exclude Site</div>
        <input class="seo-input" id="seo-exclude-site" type="text" placeholder="e.g. pinterest.com" />
      </div>

      <div class="seo-section">
        <div class="seo-section-label">File Type</div>
        <div class="seo-chip-row" id="seo-filetype">
          <span class="seo-chip" data-val="pdf">PDF</span>
          <span class="seo-chip" data-val="doc">DOC</span>
          <span class="seo-chip" data-val="docx">DOCX</span>
          <span class="seo-chip" data-val="xls">XLS</span>
          <span class="seo-chip" data-val="xlsx">XLSX</span>
          <span class="seo-chip" data-val="ppt">PPT</span>
          <span class="seo-chip" data-val="csv">CSV</span>
          <span class="seo-chip" data-val="txt">TXT</span>
          <span class="seo-chip" data-val="xml">XML</span>
          <span class="seo-chip" data-val="json">JSON</span>
        </div>
        <input class="seo-input" id="seo-filetype-custom" type="text" placeholder="Or type custom filetype..." style="margin-top:6px" />
      </div>

      <div class="seo-section">
        <div class="seo-section-label">Date Range</div>
        <select class="seo-select" id="seo-date-preset">
          <option value="">— any time —</option>
          <option value="d">Past 24 hours</option>
          <option value="w">Past week</option>
          <option value="m">Past month</option>
          <option value="y">Past year</option>
          <option value="custom">Custom range...</option>
        </select>
        <div class="seo-date-row" id="seo-custom-date" style="display:none;margin-top:6px">
          <div class="seo-date-wrap">
            <small>From</small>
            <input class="seo-input" type="date" id="seo-date-from" />
          </div>
          <div class="seo-date-wrap">
            <small>To</small>
            <input class="seo-input" type="date" id="seo-date-to" />
          </div>
        </div>
      </div>

      <div class="seo-section">
        <div class="seo-section-label">Related / Cache</div>
        <div class="seo-chip-row" id="seo-special">
          <span class="seo-chip" data-val="related">related:</span>
          <span class="seo-chip" data-val="cache">cache:</span>
          <span class="seo-chip" data-val="info">info:</span>
        </div>
        <input class="seo-input" id="seo-special-val" type="text" placeholder="Enter URL or domain..." style="margin-top:6px;display:none" />
      </div>

      <div class="seo-section">
        <div class="seo-section-label">Exclude Words</div>
        <input class="seo-input" id="seo-exclude" type="text" placeholder='e.g. spam, ads, "click here"' />
      </div>

      <div class="seo-section">
        <div class="seo-section-label">Exact Phrase</div>
        <input class="seo-input" id="seo-exact" type="text" placeholder='e.g. content marketing tips' />
      </div>

      <div>
        <div class="seo-query-preview-label">Query Preview</div>
        <div class="seo-query-preview" id="seo-preview">—</div>
      </div>

    </div>
    <div class="seo-footer">
      <button class="seo-btn seo-btn-primary" id="seo-apply">⌕ Apply Filters</button>
      <button class="seo-btn seo-btn-secondary" id="seo-reset">✕ Reset All</button>
    </div>
  `;
  document.body.appendChild(sidebar);

  // ─── Toggle Button ───────────────────────────────────────────────────────────
  const toggle = document.createElement('button');
  toggle.id = 'seo-sidebar-toggle';
  toggle.innerHTML = `<span class="toggle-arrow">◀</span>SEO Filters`;
  document.body.appendChild(toggle);

  // ─── Toast ───────────────────────────────────────────────────────────────────
  const toast = document.createElement('div');
  toast.className = 'seo-toast';
  document.body.appendChild(toast);
  function showToast(msg) {
    toast.textContent = msg;
    toast.classList.add('show');
    setTimeout(() => toast.classList.remove('show'), 2200);
  }

  // ─── State ───────────────────────────────────────────────────────────────────
  let isOpen = false;
  let selectedFiletype = null;
  let selectedKwLocation = null;
  let selectedSpecial = null;

  // ─── Toggle open/close ───────────────────────────────────────────────────────
  toggle.addEventListener('click', () => {
    isOpen = !isOpen;
    sidebar.classList.toggle('open', isOpen);
    toggle.querySelector('.toggle-arrow').textContent = isOpen ? '▶' : '◀';
  });

  // ─── Chip logic ──────────────────────────────────────────────────────────────
  function setupChips(containerId, onSelect) {
    const container = document.getElementById(containerId);
    container.querySelectorAll('.seo-chip').forEach(chip => {
      chip.addEventListener('click', () => {
        const isActive = chip.classList.contains('active');
        container.querySelectorAll('.seo-chip').forEach(c => c.classList.remove('active'));
        if (!isActive) {
          chip.classList.add('active');
          onSelect(chip.dataset.val);
        } else {
          onSelect(null);
        }
        updatePreview();
      });
    });
  }

  setupChips('seo-filetype', v => { selectedFiletype = v; });
  setupChips('seo-kw-location', v => { selectedKwLocation = v; });
  setupChips('seo-special', v => {
    selectedSpecial = v;
    document.getElementById('seo-special-val').style.display = v ? 'block' : 'none';
  });

  // ─── Custom filetype overrides chip ─────────────────────────────────────────
  document.getElementById('seo-filetype-custom').addEventListener('input', () => {
    if (document.getElementById('seo-filetype-custom').value.trim()) {
      document.getElementById('seo-filetype').querySelectorAll('.seo-chip').forEach(c => c.classList.remove('active'));
      selectedFiletype = null;
    }
    updatePreview();
  });

  // ─── Date preset ─────────────────────────────────────────────────────────────
  document.getElementById('seo-date-preset').addEventListener('change', () => {
    const v = document.getElementById('seo-date-preset').value;
    document.getElementById('seo-custom-date').style.display = v === 'custom' ? 'grid' : 'none';
    updatePreview();
  });

  // ─── Live preview on all inputs ──────────────────────────────────────────────
  ['seo-keyword','seo-site','seo-exclude-site','seo-exclude','seo-exact','seo-special-val','seo-date-from','seo-date-to'].forEach(id => {
    const el = document.getElementById(id);
    if (el) el.addEventListener('input', updatePreview);
  });

  // ─── Build query string ──────────────────────────────────────────────────────
  function buildQuery() {
    const parts = [];
    const keyword = document.getElementById('seo-keyword').value.trim();
    const site = document.getElementById('seo-site').value.trim();
    const excludeSite = document.getElementById('seo-exclude-site').value.trim();
    const exact = document.getElementById('seo-exact').value.trim();
    const exclude = document.getElementById('seo-exclude').value.trim();
    const customFT = document.getElementById('seo-filetype-custom').value.trim();
    const specialVal = document.getElementById('seo-special-val').value.trim();
    const datePreset = document.getElementById('seo-date-preset').value;
    const dateFrom = document.getElementById('seo-date-from').value;
    const dateTo = document.getElementById('seo-date-to').value;

    if (exact) parts.push(`"${exact}"`);
    if (keyword) {
      if (selectedKwLocation) {
        parts.push(`${selectedKwLocation}:${keyword}`);
      } else {
        parts.push(keyword);
      }
    }
    if (site) parts.push(`site:${site}`);
    if (excludeSite) parts.push(`-site:${excludeSite}`);

    const ft = customFT || selectedFiletype;
    if (ft) parts.push(`filetype:${ft}`);

    if (exclude) {
      exclude.split(',').map(w => w.trim()).filter(Boolean).forEach(w => parts.push(`-${w}`));
    }
    if (selectedSpecial && specialVal) {
      parts.push(`${selectedSpecial}:${specialVal}`);
    }

    return { query: parts.join(' '), datePreset, dateFrom, dateTo };
  }

  function updatePreview() {
    const { query } = buildQuery();
    document.getElementById('seo-preview').textContent = query || '—';
  }

  // ─── Apply Filters ───────────────────────────────────────────────────────────
  document.getElementById('seo-apply').addEventListener('click', () => {
    const { query, datePreset, dateFrom, dateTo } = buildQuery();
    if (!query && !datePreset) { showToast('Add at least one filter first.'); return; }

    const params = new URLSearchParams(window.location.search);
    params.set('q', query || params.get('q') || '');

    // Date filter via tbs param
    if (datePreset && datePreset !== 'custom') {
      params.set('tbs', `qdr:${datePreset}`);
    } else if (datePreset === 'custom' && dateFrom && dateTo) {
      const fmt = d => d.replace(/-/g, '/');
      params.set('tbs', `cdr:1,cd_min:${fmt(dateFrom)},cd_max:${fmt(dateTo)}`);
    } else {
      params.delete('tbs');
    }

    params.delete('start'); // reset pagination
    window.location.href = `https://www.google.com/search?${params.toString()}`;
  });

  // ─── Reset ───────────────────────────────────────────────────────────────────
  document.getElementById('seo-reset').addEventListener('click', () => {
    ['seo-keyword','seo-site','seo-exclude-site','seo-exclude','seo-exact','seo-filetype-custom','seo-special-val'].forEach(id => {
      document.getElementById(id).value = '';
    });
    document.getElementById('seo-date-preset').value = '';
    document.getElementById('seo-date-from').value = '';
    document.getElementById('seo-date-to').value = '';
    document.getElementById('seo-custom-date').style.display = 'none';
    document.getElementById('seo-special-val').style.display = 'none';
    document.querySelectorAll('.seo-chip.active').forEach(c => c.classList.remove('active'));
    selectedFiletype = null; selectedKwLocation = null; selectedSpecial = null;
    updatePreview();
    showToast('Filters cleared.');
  });

  // ─── Auto-populate from current URL ──────────────────────────────────────────
  const currentQ = new URLSearchParams(window.location.search).get('q') || '';
  if (currentQ) {
    document.getElementById('seo-keyword').value = currentQ;
    updatePreview();
  }

})();