feat(hackernews): update user scripts
add toggleable URL panel with color coding and sorting options
This commit is contained in:
parent
b6985efad2
commit
1525cc4a32
@ -1,8 +1,8 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Hacker News URL Highlighter with Preview
|
// @name Hacker News URL Highlighter with Enhanced Panel
|
||||||
// @namespace https://news.ycombinator.com/
|
// @namespace https://news.ycombinator.com/
|
||||||
// @version 1.3
|
// @version 1.4
|
||||||
// @description Highlights URLs in comments on Hacker News, lists them in a panel, and provides export functionality. Hover previews only in comments.
|
// @description Highlights URLs in comments on Hacker News, lists them in a toggleable panel with sorting and coloring based on frequency or upvotes, and provides export functionality. Hover previews only in comments.
|
||||||
// @author MorganGeek
|
// @author MorganGeek
|
||||||
// @match https://news.ycombinator.com/item?id=*
|
// @match https://news.ycombinator.com/item?id=*
|
||||||
// @grant GM_xmlhttpRequest
|
// @grant GM_xmlhttpRequest
|
||||||
@ -14,12 +14,35 @@
|
|||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Create a floating panel for displaying URLs
|
// Configuration des couleurs en fonction des fréquences ou des upvotes
|
||||||
|
const COLORS = {
|
||||||
|
low: '#d1e7dd', // Vert pâle
|
||||||
|
medium: '#fff3cd', // Jaune pâle
|
||||||
|
high: '#f8d7da' // Rouge pâle
|
||||||
|
};
|
||||||
|
|
||||||
|
// Créer un bouton pour basculer l'affichage du panneau
|
||||||
|
const toggleButton = document.createElement('button');
|
||||||
|
toggleButton.textContent = 'Afficher les URLs';
|
||||||
|
toggleButton.style.position = 'fixed';
|
||||||
|
toggleButton.style.bottom = '10px';
|
||||||
|
toggleButton.style.right = '10px';
|
||||||
|
toggleButton.style.zIndex = 1000;
|
||||||
|
toggleButton.style.padding = '10px 15px';
|
||||||
|
toggleButton.style.backgroundColor = '#ff6600';
|
||||||
|
toggleButton.style.color = '#fff';
|
||||||
|
toggleButton.style.border = 'none';
|
||||||
|
toggleButton.style.borderRadius = '5px';
|
||||||
|
toggleButton.style.cursor = 'pointer';
|
||||||
|
document.body.appendChild(toggleButton);
|
||||||
|
|
||||||
|
// Créer un panneau flottant pour afficher les URLs (caché par défaut)
|
||||||
const panel = document.createElement('div');
|
const panel = document.createElement('div');
|
||||||
panel.id = 'url-panel';
|
panel.id = 'url-panel';
|
||||||
panel.style.position = 'fixed';
|
panel.style.position = 'fixed';
|
||||||
panel.style.top = '10px';
|
panel.style.top = '10px';
|
||||||
panel.style.right = '10px';
|
panel.style.right = '10px';
|
||||||
|
panel.style.width = '300px';
|
||||||
panel.style.backgroundColor = '#fff';
|
panel.style.backgroundColor = '#fff';
|
||||||
panel.style.border = '1px solid #ccc';
|
panel.style.border = '1px solid #ccc';
|
||||||
panel.style.borderRadius = '5px';
|
panel.style.borderRadius = '5px';
|
||||||
@ -28,46 +51,74 @@
|
|||||||
panel.style.maxHeight = '80vh';
|
panel.style.maxHeight = '80vh';
|
||||||
panel.style.overflowY = 'auto';
|
panel.style.overflowY = 'auto';
|
||||||
panel.style.zIndex = 1000;
|
panel.style.zIndex = 1000;
|
||||||
panel.innerHTML = '<h4>URLs</h4><ul id="url-list" style="margin: 0; padding: 0; list-style: none;"></ul><button id="copy-urls" style="margin-top: 10px; display: block; width: 100%;">Copy URLs</button>';
|
panel.style.display = 'none'; // Caché par défaut
|
||||||
|
panel.innerHTML = `
|
||||||
|
<h4>URLs</h4>
|
||||||
|
<div style="margin-bottom: 10px;">
|
||||||
|
<label for="sort-select">Trier par:</label>
|
||||||
|
<select id="sort-select" style="width: 100%; padding: 5px; margin-top: 5px;">
|
||||||
|
<option value="frequency">Fréquence</option>
|
||||||
|
<option value="name">Nom</option>
|
||||||
|
<option value="upvotes">Upvotes</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<ul id="url-list" style="margin: 0; padding: 0; list-style: none;"></ul>
|
||||||
|
<div style="margin-top: 10px;">
|
||||||
|
<label for="limit-select">Afficher:</label>
|
||||||
|
<select id="limit-select" style="width: 100%; padding: 5px; margin-top: 5px;">
|
||||||
|
<option value="all">Tout</option>
|
||||||
|
<option value="10">Top 10</option>
|
||||||
|
<option value="20">Top 20</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button id="copy-urls" style="margin-top: 10px; display: block; width: 100%; padding: 10px; background-color: #ff6600; color: #fff; border: none; border-radius: 5px; cursor: pointer;">Copier les URLs</button>
|
||||||
|
`;
|
||||||
document.body.appendChild(panel);
|
document.body.appendChild(panel);
|
||||||
|
|
||||||
// Tooltip for previews
|
// Créer une infobulle pour les aperçus
|
||||||
const tooltip = document.createElement('div');
|
const tooltip = document.createElement('div');
|
||||||
tooltip.id = 'url-tooltip';
|
tooltip.id = 'url-tooltip';
|
||||||
tooltip.style.position = 'absolute';
|
tooltip.style.position = 'absolute';
|
||||||
tooltip.style.backgroundColor = '#fff';
|
tooltip.style.backgroundColor = '#fff';
|
||||||
tooltip.style.border = '1px solid #ccc';
|
tooltip.style.border = '1px solid #ccc';
|
||||||
tooltip.style.borderRadius = '5px';
|
tooltip.style.borderRadius = '5px';
|
||||||
tooltip.style.padding = '5px';
|
tooltip.style.padding = '10px';
|
||||||
tooltip.style.boxShadow = '0px 0px 10px rgba(0,0,0,0.2)';
|
tooltip.style.boxShadow = '0px 0px 10px rgba(0,0,0,0.2)';
|
||||||
tooltip.style.maxWidth = '300px';
|
tooltip.style.maxWidth = '300px';
|
||||||
tooltip.style.display = 'none';
|
tooltip.style.display = 'none';
|
||||||
tooltip.style.zIndex = 1001;
|
tooltip.style.zIndex = 1001;
|
||||||
document.body.appendChild(tooltip);
|
document.body.appendChild(tooltip);
|
||||||
|
|
||||||
const urlSet = new Set();
|
// Structure de données pour stocker les URLs avec leurs métadonnées
|
||||||
|
const urlData = {};
|
||||||
|
|
||||||
// Extract URLs from comments and highlight them
|
// Extraire les URLs des commentaires et les mettre en évidence
|
||||||
const comments = document.querySelectorAll('.commtext');
|
const comments = document.querySelectorAll('.commtext');
|
||||||
comments.forEach(comment => {
|
comments.forEach(comment => {
|
||||||
const links = comment.querySelectorAll('a[href]');
|
const links = comment.querySelectorAll('a[href]');
|
||||||
links.forEach(link => {
|
links.forEach(link => {
|
||||||
const url = link.href;
|
const url = link.href;
|
||||||
if (!urlSet.has(url)) {
|
if (!urlData[url]) {
|
||||||
urlSet.add(url);
|
urlData[url] = {
|
||||||
link.style.backgroundColor = '#ffff99'; // Highlight the link
|
url: url,
|
||||||
|
count: 1,
|
||||||
|
upvotes: 0 // Placeholder, à implémenter si les upvotes sont disponibles
|
||||||
|
};
|
||||||
|
link.style.backgroundColor = '#ffff99'; // Couleur de surbrillance initiale
|
||||||
|
|
||||||
// Add the URL to the floating panel
|
// Ajouter l'URL à la liste dans le panneau
|
||||||
const listItem = document.createElement('li');
|
const listItem = document.createElement('li');
|
||||||
listItem.textContent = url;
|
listItem.textContent = url;
|
||||||
listItem.style.marginBottom = '5px';
|
listItem.style.marginBottom = '5px';
|
||||||
listItem.style.wordWrap = 'break-word';
|
listItem.style.wordWrap = 'break-word';
|
||||||
listItem.style.cursor = 'pointer';
|
listItem.style.cursor = 'pointer';
|
||||||
listItem.dataset.url = url;
|
listItem.dataset.url = url;
|
||||||
document.getElementById('url-list').appendChild(listItem);
|
panel.querySelector('#url-list').appendChild(listItem);
|
||||||
|
} else {
|
||||||
|
urlData[url].count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add hover preview functionality to comment links
|
// Ajouter la fonctionnalité d'aperçu au survol des liens
|
||||||
link.addEventListener('mouseover', () => {
|
link.addEventListener('mouseover', () => {
|
||||||
GM_xmlhttpRequest({
|
GM_xmlhttpRequest({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@ -76,14 +127,14 @@
|
|||||||
tooltip.innerHTML = '';
|
tooltip.innerHTML = '';
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const doc = parser.parseFromString(response.responseText, 'text/html');
|
const doc = parser.parseFromString(response.responseText, 'text/html');
|
||||||
const title = doc.querySelector('title') ? doc.querySelector('title').innerText : 'No Title';
|
const title = doc.querySelector('title') ? doc.querySelector('title').innerText : 'Pas de titre';
|
||||||
const description = doc.querySelector('meta[name="description"]') ? doc.querySelector('meta[name="description"]').content : 'No Description';
|
const description = doc.querySelector('meta[name="description"]') ? doc.querySelector('meta[name="description"]').content : 'Pas de description';
|
||||||
|
|
||||||
tooltip.innerHTML = `<strong>${title}</strong><p>${description}</p>`;
|
tooltip.innerHTML = `<strong>${title}</strong><p>${description}</p>`;
|
||||||
tooltip.style.display = 'block';
|
tooltip.style.display = 'block';
|
||||||
},
|
},
|
||||||
onerror: () => {
|
onerror: () => {
|
||||||
tooltip.innerHTML = '<strong>Preview unavailable</strong>';
|
tooltip.innerHTML = '<strong>Aperçu indisponible</strong>';
|
||||||
tooltip.style.display = 'block';
|
tooltip.style.display = 'block';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -99,10 +150,118 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle the Copy URLs button
|
// Fonction pour mettre à jour l'affichage du panneau selon les filtres
|
||||||
document.getElementById('copy-urls').addEventListener('click', () => {
|
function updatePanel() {
|
||||||
const urlArray = Array.from(urlSet);
|
const sortBy = panel.querySelector('#sort-select').value;
|
||||||
GM_setClipboard(urlArray.join('\n'), 'text');
|
const limit = panel.querySelector('#limit-select').value;
|
||||||
alert('URLs copied to clipboard!');
|
|
||||||
|
// Convertir l'objet en tableau pour le tri
|
||||||
|
let urlsArray = Object.values(urlData);
|
||||||
|
|
||||||
|
// Trier en fonction du critère sélectionné
|
||||||
|
if (sortBy === 'frequency') {
|
||||||
|
urlsArray.sort((a, b) => b.count - a.count);
|
||||||
|
} else if (sortBy === 'name') {
|
||||||
|
urlsArray.sort((a, b) => a.url.localeCompare(b.url));
|
||||||
|
} else if (sortBy === 'upvotes') {
|
||||||
|
urlsArray.sort((a, b) => b.upvotes - a.upvotes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appliquer la limite si nécessaire
|
||||||
|
if (limit !== 'all') {
|
||||||
|
const limitNumber = parseInt(limit);
|
||||||
|
urlsArray = urlsArray.slice(0, limitNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlList = panel.querySelector('#url-list');
|
||||||
|
urlList.innerHTML = ''; // Vider la liste actuelle
|
||||||
|
|
||||||
|
// Déterminer les fréquences pour la coloration
|
||||||
|
const counts = urlsArray.map(item => item.count);
|
||||||
|
const maxCount = Math.max(...counts);
|
||||||
|
const minCount = Math.min(...counts);
|
||||||
|
|
||||||
|
urlsArray.forEach(item => {
|
||||||
|
const listItem = document.createElement('li');
|
||||||
|
listItem.textContent = item.url;
|
||||||
|
listItem.style.marginBottom = '5px';
|
||||||
|
listItem.style.wordWrap = 'break-word';
|
||||||
|
listItem.style.cursor = 'pointer';
|
||||||
|
listItem.dataset.url = item.url;
|
||||||
|
|
||||||
|
// Définir la couleur en fonction de la fréquence
|
||||||
|
let color;
|
||||||
|
const ratio = (item.count - minCount) / (maxCount - minCount + 1);
|
||||||
|
if (ratio > 0.66) {
|
||||||
|
color = COLORS.high;
|
||||||
|
} else if (ratio > 0.33) {
|
||||||
|
color = COLORS.medium;
|
||||||
|
} else {
|
||||||
|
color = COLORS.low;
|
||||||
|
}
|
||||||
|
listItem.style.backgroundColor = color;
|
||||||
|
|
||||||
|
// Ajouter l'événement de clic pour copier l'URL
|
||||||
|
listItem.addEventListener('click', () => {
|
||||||
|
GM_setClipboard(item.url, 'text');
|
||||||
|
alert('URL copiée dans le presse-papiers!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
urlList.appendChild(listItem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialiser le panneau
|
||||||
|
updatePanel();
|
||||||
|
|
||||||
|
// Écouter les changements dans les sélecteurs de tri et de limite
|
||||||
|
panel.querySelector('#sort-select').addEventListener('change', updatePanel);
|
||||||
|
panel.querySelector('#limit-select').addEventListener('change', updatePanel);
|
||||||
|
|
||||||
|
// Gérer le bouton de bascule pour afficher/masquer le panneau
|
||||||
|
toggleButton.addEventListener('click', () => {
|
||||||
|
if (panel.style.display === 'none') {
|
||||||
|
panel.style.display = 'block';
|
||||||
|
toggleButton.textContent = 'Masquer les URLs';
|
||||||
|
} else {
|
||||||
|
panel.style.display = 'none';
|
||||||
|
toggleButton.textContent = 'Afficher les URLs';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Gérer le bouton "Copier les URLs"
|
||||||
|
panel.querySelector('#copy-urls').addEventListener('click', () => {
|
||||||
|
const urlArray = Object.keys(urlData);
|
||||||
|
GM_setClipboard(urlArray.join('\n'), 'text');
|
||||||
|
alert('URLs copiées dans le presse-papiers!');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Optionnel : Collecter les upvotes des commentaires pour chaque URL
|
||||||
|
// Note : Hacker News n'expose pas directement les upvotes des commentaires via le DOM.
|
||||||
|
// Si vous avez une méthode pour obtenir les upvotes, vous pouvez l'implémenter ici.
|
||||||
|
// Par exemple, si les points des commentaires sont visibles, vous pouvez les extraire et les attribuer aux URLs.
|
||||||
|
|
||||||
|
// Exemple de récupération des points des commentaires (si disponibles)
|
||||||
|
const commentRows = document.querySelectorAll('tr.comtr');
|
||||||
|
commentRows.forEach(row => {
|
||||||
|
const commentLink = row.querySelector('a[href^="item?id="]');
|
||||||
|
if (commentLink) {
|
||||||
|
const pointsSpan = row.querySelector('.score');
|
||||||
|
const points = pointsSpan ? parseInt(pointsSpan.textContent) : 0;
|
||||||
|
const comment = row.querySelector('.commtext');
|
||||||
|
if (comment) {
|
||||||
|
const links = comment.querySelectorAll('a[href]');
|
||||||
|
links.forEach(link => {
|
||||||
|
const url = link.href;
|
||||||
|
if (urlData[url]) {
|
||||||
|
urlData[url].upvotes += points;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mettre à jour le panneau après avoir collecté les upvotes
|
||||||
|
updatePanel();
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
Loading…
Reference in New Issue
Block a user