Skip to content

Instantly share code, notes, and snippets.

@bquast
Created November 15, 2025 23:23
Show Gist options
  • Select an option

  • Save bquast/b93654f6b37aa0a4b8904cc041416500 to your computer and use it in GitHub Desktop.

Select an option

Save bquast/b93654f6b37aa0a4b8904cc041416500 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Grokipedia Search Result Copy URL
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Adds a copy URL button to each search result on the Grokipedia search results page.
// @author Bastiaan Quast
// @match https://grokipedia.com/search*
// @grant GM_setClipboard
// @grant GM_addStyle
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// SVG icon for the copy button
const COPY_ICON_SVG = `
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path>
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect>
</svg>
`;
// Add CSS styles for the new button
GM_addStyle(`
.grok-copy-url-btn {
background: none;
border: none;
cursor: pointer;
padding: 4px;
margin-left: 8px; /* Add some space next to the title */
border-radius: 4px;
color: var(--fg-secondary); /* Use site's CSS variable */
display: inline-flex;
align-items: center;
/* Ensure it's not hidden by parent styles */
z-index: 10;
position: relative;
}
.grok-copy-url-btn:hover {
background-color: var(--overlay-hover); /* Use site's CSS variable */
color: var(--fg-primary); /* Use site's CSS variable */
}
.grok-copy-url-btn svg {
width: 16px;
height: 16px;
}
`);
// Function to add the copy button to a single search result element
function addCopyButton(resultElement) {
// Find the element containing the title text
const titleSpan = resultElement.querySelector('span.line-clamp-1 > span');
if (!titleSpan) return;
// Find the container to inject the button into
const titleContainer = titleSpan.closest('.flex.items-center.gap-2');
// Check if button already exists
if (!titleContainer || titleContainer.querySelector('.grok-copy-url-btn')) {
return;
}
// 1. Get the title and format it for the URL
const title = titleSpan.textContent.trim();
if (!title) return; // Skip if title is empty
const articleUrl = `https://grokipedia.com/page/${title.replace(/ /g, '_')}`;
// 2. Create the button
const button = document.createElement('button');
button.innerHTML = COPY_ICON_SVG;
button.className = 'grok-copy-url-btn';
button.title = 'Copy article URL';
// 3. Add click event listener
button.addEventListener('click', (e) => {
e.preventDefault(); // Stop the click from navigating to the article
e.stopPropagation(); // Stop the click from propagating to the parent link
// Use GM_setClipboard to copy the URL
GM_setClipboard(articleUrl, 'text');
// Provide visual feedback to the user
button.innerHTML = 'Copied!';
setTimeout(() => {
button.innerHTML = COPY_ICON_SVG;
}, 1500);
});
// 4. Inject the button into the DOM
titleContainer.appendChild(button);
}
// Function to find and process all search results
function processResults() {
// Select all result items that haven't been processed yet
const results = document.querySelectorAll('div.rounded-md.p-2.cursor-pointer:not([data-copy-btn-added])');
results.forEach(result => {
// Mark as processed to avoid re-adding buttons
result.dataset.copyBtnAdded = 'true';
addCopyButton(result);
});
}
// Poll the page every 500ms to find new results
// This is more robust for dynamic pages (SPAs) than a MutationObserver
setInterval(processResults, 500);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment