// ==UserScript== // @name Hacker News URL Highlighter with Preview // @namespace https://news.ycombinator.com/ // @version 1.3 // @description Highlights URLs in comments on Hacker News, lists them in a panel, and provides export functionality. Hover previews only in comments. // @author MorganGeek // @match https://news.ycombinator.com/item?id=* // @grant GM_xmlhttpRequest // @grant GM_setClipboard // @connect * // @run-at document-end // ==/UserScript== (function () { 'use strict'; // Create a floating panel for displaying URLs const panel = document.createElement('div'); panel.id = 'url-panel'; panel.style.position = 'fixed'; panel.style.top = '10px'; panel.style.right = '10px'; panel.style.backgroundColor = '#fff'; panel.style.border = '1px solid #ccc'; panel.style.borderRadius = '5px'; panel.style.padding = '10px'; panel.style.boxShadow = '0px 0px 10px rgba(0,0,0,0.2)'; panel.style.maxHeight = '80vh'; panel.style.overflowY = 'auto'; panel.style.zIndex = 1000; panel.innerHTML = '

URLs

'; document.body.appendChild(panel); // Tooltip for previews const tooltip = document.createElement('div'); tooltip.id = 'url-tooltip'; tooltip.style.position = 'absolute'; tooltip.style.backgroundColor = '#fff'; tooltip.style.border = '1px solid #ccc'; tooltip.style.borderRadius = '5px'; tooltip.style.padding = '5px'; tooltip.style.boxShadow = '0px 0px 10px rgba(0,0,0,0.2)'; tooltip.style.maxWidth = '300px'; tooltip.style.display = 'none'; tooltip.style.zIndex = 1001; document.body.appendChild(tooltip); const urlSet = new Set(); // Extract URLs from comments and highlight them const comments = document.querySelectorAll('.commtext'); comments.forEach(comment => { const links = comment.querySelectorAll('a[href]'); links.forEach(link => { const url = link.href; if (!urlSet.has(url)) { urlSet.add(url); link.style.backgroundColor = '#ffff99'; // Highlight the link // Add the URL to the floating panel const listItem = document.createElement('li'); listItem.textContent = url; listItem.style.marginBottom = '5px'; listItem.style.wordWrap = 'break-word'; listItem.style.cursor = 'pointer'; listItem.dataset.url = url; document.getElementById('url-list').appendChild(listItem); } // Add hover preview functionality to comment links link.addEventListener('mouseover', () => { GM_xmlhttpRequest({ method: 'GET', url: url, onload: response => { tooltip.innerHTML = ''; const parser = new DOMParser(); const doc = parser.parseFromString(response.responseText, 'text/html'); const title = doc.querySelector('title') ? doc.querySelector('title').innerText : 'No Title'; const description = doc.querySelector('meta[name="description"]') ? doc.querySelector('meta[name="description"]').content : 'No Description'; tooltip.innerHTML = `${title}

${description}

`; tooltip.style.display = 'block'; }, onerror: () => { tooltip.innerHTML = 'Preview unavailable'; tooltip.style.display = 'block'; } }); const rect = link.getBoundingClientRect(); tooltip.style.top = `${rect.bottom + window.scrollY + 5}px`; tooltip.style.left = `${rect.left + window.scrollX}px`; }); link.addEventListener('mouseout', () => { tooltip.style.display = 'none'; }); }); }); // Handle the Copy URLs button document.getElementById('copy-urls').addEventListener('click', () => { const urlArray = Array.from(urlSet); GM_setClipboard(urlArray.join('\n'), 'text'); alert('URLs copied to clipboard!'); }); })();