Skip to content

Instantly share code, notes, and snippets.

@overflowy
Last active September 3, 2025 08:08
Show Gist options
  • Select an option

  • Save overflowy/628223ea43782c3bd102dfe3b9163a34 to your computer and use it in GitHub Desktop.

Select an option

Save overflowy/628223ea43782c3bd102dfe3b9163a34 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Google Search +
// @namespace http://tampermonkey.net/
// @version 1.9
// @description Extend Google Search with additional options.
// @author overflowy
// @match https://www.google.com/search*
// @match https://www.google.*/search*
// @run-at document-start
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Function to extract search query from URL
function getSearchQuery() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('q') || '';
}
// Function to create button container
function createButtonContainer() {
if (document.getElementById('search-buttons-container')) {
return document.getElementById('search-buttons-container');
}
const container = document.createElement('div');
container.id = 'search-buttons-container';
container.style.cssText = `
display: flex;
gap: 8px;
margin-bottom: 16px;
align-items: center;
`;
return container;
}
// Universal function to create buttons (both single and split)
function createButton(config) {
// Check if button already exists
if (document.getElementById(config.id)) return null;
// Handle split button
if (config.split) {
const buttonContainer = document.createElement('div');
buttonContainer.id = config.id;
buttonContainer.style.cssText = `
display: inline-flex;
border-radius: 3px;
overflow: hidden;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
transition: all 0.2s ease;
`;
config.split.forEach((splitConfig, index) => {
const splitButton = document.createElement('button');
splitButton.innerHTML = splitConfig.text;
splitButton.style.cssText = `
background-color: ${config.backgroundColor};
color: white;
border: none;
padding: 6px 10px;
cursor: pointer;
font-size: 12px;
display: inline-flex;
align-items: center;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-weight: 500;
line-height: 1;
${index < config.split.length - 1 ? 'border-right: 1px solid rgba(255,255,255,0.2);' : ''}
`;
// Add hover effects
splitButton.addEventListener('mouseenter', () => {
splitButton.style.backgroundColor = config.hoverColor;
buttonContainer.style.transform = 'translateY(-1px)';
buttonContainer.style.boxShadow = '0 2px 4px rgba(0,0,0,0.15)';
});
splitButton.addEventListener('mouseleave', () => {
splitButton.style.backgroundColor = config.backgroundColor;
buttonContainer.style.transform = 'translateY(0)';
buttonContainer.style.boxShadow = '0 1px 2px rgba(0,0,0,0.1)';
});
// Add click handler
splitButton.addEventListener('click', splitConfig.onClick);
buttonContainer.appendChild(splitButton);
});
return buttonContainer;
} else {
// Handle single button
const button = document.createElement('button');
button.id = config.id;
button.innerHTML = config.text;
button.style.cssText = `
background-color: ${config.backgroundColor};
color: white;
border: none;
padding: 6px 12px;
border-radius: 3px;
cursor: pointer;
font-size: 12px;
display: inline-flex;
align-items: center;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-weight: 500;
transition: all 0.2s ease;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
line-height: 1;
white-space: nowrap;
`;
// Add hover effects
button.addEventListener('mouseenter', () => {
button.style.backgroundColor = config.hoverColor;
button.style.transform = 'translateY(-1px)';
button.style.boxShadow = '0 2px 4px rgba(0,0,0,0.15)';
});
button.addEventListener('mouseleave', () => {
button.style.backgroundColor = config.backgroundColor;
button.style.transform = 'translateY(0)';
button.style.boxShadow = '0 1px 2px rgba(0,0,0,0.1)';
});
// Add click handler
button.addEventListener('click', config.onClick);
return button;
}
}
// Function to add all buttons
function addSearchButtons() {
const query = getSearchQuery();
if (!query) return;
// Create button container
const container = createButtonContainer();
// Button configurations
const buttonConfigs = [
{
id: 'reddit-button',
text: 'Reddit',
backgroundColor: '#ff4500',
hoverColor: '#e63d00',
onClick: () => {
const currentQuery = getSearchQuery();
let redditQuery;
// Check if site:reddit.com is already in the query
if (currentQuery.toLowerCase().includes('site:reddit.com')) {
redditQuery = currentQuery;
} else {
redditQuery = `${currentQuery} site:reddit.com`;
}
const googleUrl = `${window.location.origin}/search?q=${encodeURIComponent(redditQuery)}`;
window.location.href = googleUrl;
}
},
{
id: 'perplexity-button',
text: 'Perplexity',
backgroundColor: '#32b8c6',
hoverColor: '#2a9da8',
onClick: () => {
const perplexityUrl = `https://www.perplexity.ai/search?q=${encodeURIComponent(query)}`;
window.open(perplexityUrl, '_blank');
}
},
{
id: 'hn-button-container',
backgroundColor: '#ff6600',
hoverColor: '#e55a00',
split: [
{
text: 'HN Posts',
onClick: () => {
const algoliaUrl = `https://hn.algolia.com/?dateRange=all&page=0&prefix=false&query=${encodeURIComponent(query)}&sort=byPopularity&type=story`;
window.open(algoliaUrl, '_blank');
}
},
{
text: 'Ask HN',
onClick: () => {
const hackerSearchUrl = `https://hackersearch.net/ask?q=${encodeURIComponent(query)}`;
window.open(hackerSearchUrl, '_blank');
}
}
]
},
{
id: 'youtube-button',
text: 'YouTube',
backgroundColor: '#ff0000',
hoverColor: '#cc0000',
onClick: () => {
const youtubeUrl = `https://www.youtube.com/results?search_query=${encodeURIComponent(query)}`;
window.open(youtubeUrl, '_blank');
}
},
{
id: 'maps-button',
text: 'Maps',
backgroundColor: '#34a853',
hoverColor: '#2d8f47',
onClick: () => {
const mapsUrl = `https://www.google.com/maps/search/${encodeURIComponent(query)}`;
window.open(mapsUrl, '_blank');
}
},
{
id: 'annas-archive-button',
text: "Anna's Archive",
backgroundColor: '#8b5a3c',
hoverColor: '#744a33',
onClick: () => {
const annasArchiveUrl = `https://annas-archive.org/search?q=${encodeURIComponent(query)}`;
window.open(annasArchiveUrl, '_blank');
}
}
];
// Create and add all buttons
buttonConfigs.forEach(config => {
const button = createButton(config);
if (button) {
container.appendChild(button);
}
});
// Only add container if it has buttons and isn't already in DOM
if (container.children.length > 0 && !document.getElementById('search-buttons-container')) {
// Find a suitable place to insert the container
const searchContainer = document.querySelector('#search') ||
document.querySelector('#main') ||
document.querySelector('#center_col');
if (searchContainer) {
// Try to place it near the search results info
const resultStats = document.querySelector('#result-stats');
if (resultStats) {
resultStats.parentNode.insertBefore(container, resultStats.nextSibling);
} else {
// Fallback: insert at the beginning of the search container
searchContainer.insertBefore(container, searchContainer.firstChild);
}
}
}
}
// Enhanced initialization with multiple strategies
function initializeScript() {
// Try immediate execution
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', addSearchButtons);
} else {
addSearchButtons();
}
// Also try when page fully loads
if (document.readyState !== 'complete') {
window.addEventListener('load', addSearchButtons);
}
// Enhanced mutation observer for faster detection
const observer = new MutationObserver((mutations) => {
let shouldCheck = false;
for (const mutation of mutations) {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
for (const node of mutation.addedNodes) {
if (node.nodeType === 1 && // Element node
(node.id === 'search' ||
node.id === 'main' ||
node.querySelector && node.querySelector('#search, #main'))) {
shouldCheck = true;
break;
}
}
}
if (shouldCheck) break;
}
if (shouldCheck) {
setTimeout(addSearchButtons, 10);
}
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
// URL change detection (for SPA behavior)
let lastUrl = location.href;
const urlObserver = new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
setTimeout(addSearchButtons, 50);
}
});
urlObserver.observe(document, {subtree: true, childList: true});
}
// Start immediately
initializeScript();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment