Adds a powerful SEO filter sidebar to Google Search with filetype, date range, site filter, keyword location, and more.
// ==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();
}
})();