Skip to content

Instantly share code, notes, and snippets.

@shuantsu
Last active May 12, 2026 10:48
Show Gist options
  • Select an option

  • Save shuantsu/d550c00b228271c9bdd7195c4244276c to your computer and use it in GitHub Desktop.

Select an option

Save shuantsu/d550c00b228271c9bdd7195c4244276c to your computer and use it in GitHub Desktop.
make tabnews better
// ==UserScript==
// @name TABNEWS BETTER DESIGN
// @namespace http://tampermonkey.net/
// @version 2026-05-12
// @description try to take over the world!
// @author You
// @match https://www.tabnews.com.br/
// @icon https://www.google.com/s2/favicons?sz=64&domain=github.com
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 1. Configurações de Identidade Visual (Avatares e Cores)
const AVATAR_COLORS = ['#6f42c1', '#0969da', '#1a7f37', '#d73a49', '#6e40c9', '#b45309', '#0f6e56', '#c11574'];
function avatarColor(n) {
let h = 0;
for (let i = 0; i < n.length; i++) h = (h * 31 + n.charCodeAt(i)) & 0xffffff;
return AVATAR_COLORS[Math.abs(h) % AVATAR_COLORS.length];
}
function initials(n) {
return n.replace(/[^a-zA-Z]/g, '').slice(0, 2).toUpperCase() || '??';
}
// 2. CSS Moderno: Masonry Real + Full Width + Estilo de Cards
const style = document.createElement('style');
style.innerHTML = `
main.gVvXqe { max-width: 100% !important; width: 100% !important; padding: 20px !important; background: #f6f8fa !important; }
ol.bKvFHa { display: none !important; }
#tn-masonry-grid {
display: grid !important;
grid-template-columns: repeat(auto-fill, minmax(380px, 1fr)) !important;
grid-auto-rows: 1px !important;
gap: 0 24px !important;
width: 100% !important;
align-items: start !important;
}
.tn-creator-card {
background: #ffffff !important;
border: 1px solid #d0d7de !important;
border-radius: 12px !important;
margin-bottom: 24px !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.05) !important;
overflow: hidden !important;
padding: 0 !important;
}
.card-inner { padding: 24px !important; display: flex !important; flex-direction: column !important; }
/* Cabeçalho do Card com Avatar */
.tn-card-header {
display: flex !important;
align-items: center !important;
gap: 12px !important;
margin-bottom: 15px !important;
}
.tn-avatar {
width: 38px; height: 38px; border-radius: 50%;
color: #fff; display: flex; align-items: center; justify-content: center;
font-size: 14px; font-weight: 800; flex-shrink: 0;
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.1);
}
.tn-creator-name {
background: #6f42c1; color: #fff; padding: 4px 12px;
border-radius: 6px; font-weight: 800; font-size: 1.1rem;
text-decoration: none !important;
}
/* Divisão de Posts */
.tn-post-item {
padding: 16px 0 !important;
border-bottom: 2px solid #444 !important;
}
.tn-post-item:last-child { border-bottom: none !important; }
.tn-post-title {
font-weight: 700; color: #0969da; text-decoration: none;
font-size: 1.1rem; display: block; margin-bottom: 5px;
}
.tn-post-title:hover { text-decoration: underline; }
.tn-post-meta { font-size: 0.85rem; color: #57606a; line-height: 1.4; }
.tn-post-meta b { color: #0969da; }
`;
document.head.appendChild(style);
const list = document.querySelector('ol.bKvFHa');
if (!list) return;
// Função de ajuste de altura (Masonry)
function resizeGridItem(item) {
const inner = item.querySelector('.card-inner');
if (!inner) return;
const contentHeight = inner.getBoundingClientRect().height;
const rowSpan = Math.ceil(contentHeight + 24);
item.style.gridRowEnd = "span " + rowSpan;
}
function renderEnhancer() {
const posts = [];
list.querySelectorAll('li').forEach(li => {
const titleEl = li.querySelector('a.hnyTjC') || li.querySelector('a.kMiBSO') || li.querySelector('a');
const authorEl = li.querySelector('address a') || li.querySelector('a.hQluGh') || li.querySelector('a[href^="/"]');
const coinsEl = li.querySelector('span[role="tooltip"]');
const timeEl = li.querySelector('time');
if (!titleEl || !authorEl) return;
const authorName = authorEl.textContent.trim().replace('@', '');
posts.push({
title: titleEl.textContent.trim(),
href: titleEl.getAttribute('href'),
author: authorName,
authorHref: authorEl.getAttribute('href'),
coins: coinsEl ? coinsEl.textContent.trim() : '0 tabcoins',
time: timeEl ? timeEl.textContent.trim() : '',
});
});
const groups = {};
posts.forEach(p => {
if (!groups[p.author]) groups[p.author] = { name: p.author, href: p.authorHref, posts: [] };
groups[p.author].posts.push(p);
});
const sortedAuthors = Object.values(groups).sort((a, b) => b.posts.length - a.posts.length);
let grid = document.getElementById('tn-masonry-grid');
if (!grid) {
grid = document.createElement('div');
grid.id = 'tn-masonry-grid';
list.parentNode.insertBefore(grid, list);
}
grid.innerHTML = '';
sortedAuthors.forEach(g => {
const color = avatarColor(g.name);
const card = document.createElement('div');
card.className = 'tn-creator-card';
const postsHtml = g.posts.map(p => `
<div class="tn-post-item">
<a class="tn-post-title" href="${p.href}">${p.title}</a>
<div class="tn-post-meta"><b>${p.coins}</b> · ${p.time}</div>
</div>
`).join('');
card.innerHTML = `
<div class="card-inner">
<div class="tn-card-header">
<div class="tn-avatar" style="background:${color}">${initials(g.name)}</div>
<a href="${g.href}" class="tn-creator-name">${g.name}</a>
<span style="margin-left:auto; font-size:0.8rem; color:#888;">${g.posts.length} posts</span>
</div>
<div>${postsHtml}</div>
</div>
`;
grid.appendChild(card);
});
requestAnimationFrame(() => {
document.querySelectorAll('.tn-creator-card').forEach(resizeGridItem);
});
}
// Execução única
renderEnhancer();
// Re-calcula Masonry se a janela mudar de tamanho
window.addEventListener('resize', () => {
document.querySelectorAll('.tn-creator-card').forEach(resizeGridItem);
});
console.log('%c TabNews Masonry Enhancer Ativado ', 'background:#6f42c1;color:#fff;font-weight:bold;padding:4px;');
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment