// ==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!');
});
})();