Скрипт для фильтрации авторов на дэшборде моделей.
В последнее время стало очень много контента, что засоряет панель. На сайте родная фильтрация - примитивна. Надо с этим что-то делать. Deepseek помог.
Скрипт добавляем в Tampermonkey. В панели, которая появляется внизу справа на странице - список авторов, которых не отображать. Добавлять можно также alt+клик на имени автора.
Всем приятного дэшборда с моделями.
// ==UserScript==
// @name 3DToday — ЧС авторов
// @namespace https://3dtoday.ru/
// @version 3.0
// @description Скрывает карточки моделей 3dtoday.ru через CSS :has(). Alt+клик по аватарке — добавить в ЧС.
// @author You
// @match https://3dtoday.ru/*
// @grant GM_registerMenuCommand
// ==/UserScript==
(function () {
'use strict';
const TILE_SELECTOR = 'div.tile';
const STORAGE_KEY = 'blocked_authors_3dt_v3';
const AUTHOR_PATH_RE = /\/(?:blogs|users|author)\/([^\/?#]+)/i;
const AUTHOR_LINK_SELECTOR = 'a[href*="/blogs/"], a[href*="/users/"], a[href*="/author/"]';
// --- Хранилище (с миграцией из старых ключей v1/v2) ---
let blocked = [];
try {
const v3 = localStorage.getItem(STORAGE_KEY);
const v2 = localStorage.getItem('blocked_authors_3dt_v2');
const v1 = localStorage.getItem('blocked_authors_3dt_v1');
const raw = v3 || v2 || v1 || '[]';
blocked = JSON.parse(raw) || [];
} catch (e) { blocked = []; }
blocked = [...new Set(blocked.map(x => String(x).toLowerCase()))];
function extractSlug(href) {
if (!href) return null;
const m = String(href).match(AUTHOR_PATH_RE);
return m ? m[1].toLowerCase() : null;
}
// --- CSS ---
const styleEl = document.createElement('style');
styleEl.id = 'bl-blocked-style';
(document.head || document.documentElement).appendChild(styleEl);
function buildCss() {
const rules = [];
blocked.forEach(slug => {
const s = slug.replace(/"/g, '\\"');
['/blogs/', '/users/', '/author/'].forEach(prefix => {
rules.push(
`div.tile:has(a[href$="${prefix}${s}"])`,
`div.tile:has(a[href*="${prefix}${s}/"])`,
`div.tile:has(a[href*="${prefix}${s}?"])`,
`div.tile:has(a[href*="${prefix}${s}#"])`
);
});
});
let css = '';
if (rules.length) {
css += rules.join(',') + ' { display: none !important; }\n';
}
// Подсказка при наведении на аватарку
css += `
a.tile-info-user { position: relative; }
a.tile-info-user:hover::after {
content: '🚫 Alt+клик';
position: absolute;
top: -22px; left: 0;
background: rgba(0,0,0,.85);
color: #fff;
font: 10px/1.2 system-ui, sans-serif;
padding: 2px 6px;
border-radius: 3px;
white-space: nowrap;
pointer-events: none;
z-index: 10000;
}
`;
styleEl.textContent = css;
}
// --- Делегированный Alt+клик по аватарке ---
document.addEventListener('click', e => {
if (!e.altKey) return;
const a = e.target.closest(AUTHOR_LINK_SELECTOR);
if (!a || !a.closest('div.tile')) return;
const slug = extractSlug(a.getAttribute('href'));
if (!slug) return;
e.preventDefault();
e.stopPropagation();
addAuthor(slug);
}, true);
// --- Панель ---
let panelEl, statusEl;
function showStatus(msg, type) {
if (!statusEl) return;
statusEl.textContent = msg;
statusEl.style.color = type === 'error' ? '#c00' : '#080';
clearTimeout(showStatus._t);
showStatus._t = setTimeout(() => { if (statusEl) statusEl.textContent = ''; }, 4000);
}
function createPanel() {
const panel = document.createElement('div');
panel.id = 'bl-authors-panel';
panel.innerHTML = `
#bl-authors-panel{position:fixed;bottom:16px;right:16px;z-index:99999;width:320px;max-height:60vh;background:#fff;color:#222;border:1px solid #ccc;border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,.2);font:13px/1.4 system-ui,sans-serif;display:flex;flex-direction:column;}
#bl-authors-panel.collapsed .bl-body{display:none;}
#bl-authors-panel .bl-header{display:flex;justify-content:space-between;align-items:center;padding:8px 10px;background:#f5f5f5;border-bottom:1px solid #ddd;border-radius:8px 8px 0 0;font-weight:600;cursor:pointer;}
#bl-authors-panel .bl-body{padding:10px;overflow-y:auto;}
#bl-authors-panel .bl-add{display:flex;gap:6px;margin-bottom:10px;}
#bl-authors-panel input[type="text"]{flex:1;padding:5px 7px;border:1px solid #ccc;border-radius:4px;font:inherit;}
#bl-authors-panel button{padding:5px 10px;border:1px solid #ccc;background:#fff;border-radius:4px;cursor:pointer;font:inherit;}
#bl-authors-panel button:hover{background:#eee;}
#bl-authors-panel ul{list-style:none;margin:0;padding:0;}
#bl-authors-panel li{display:flex;justify-content:space-between;align-items:center;padding:4px 0;border-bottom:1px dashed #eee;}
#bl-authors-panel li:last-child{border-bottom:none;}
#bl-authors-panel li button{padding:2px 6px;font-size:12px;color:#c00;border-color:#f0c0c0;}
#bl-authors-panel .bl-empty{color:#999;font-style:italic;}
#bl-authors-panel .bl-status{min-height:16px;font-size:12px;margin-top:8px;}
#bl-authors-panel .bl-hint{font-size:11px;color:#888;margin-bottom:6px;}
#bl-authors-panel .bl-stats{font-size:11px;color:#666;margin-top:6px;border-top:1px dashed #eee;padding-top:6px;}
🚫 ЧС авторов (0) ▾
Slug (напр. Animagross) или URL. Либо Alt+клик по аватарке в карточке.
Добавить
`;
document.body.appendChild(panel);
panel.querySelector('.bl-header').addEventListener('click', () => panel.classList.toggle('collapsed'));
const input = panel.querySelector('.bl-input');
const addBtn = panel.querySelector('.bl-add-btn');
const handleAdd = () => {
const raw = input.value.trim();
if (!raw) return;
const slug = extractSlug(raw) || raw.toLowerCase();
addAuthor(slug);
input.value = '';
};
addBtn.addEventListener('click', handleAdd);
input.addEventListener('keydown', e => { if (e.key === 'Enter') handleAdd(); });
statusEl = panel.querySelector('.bl-status');
return panel;
}
function renderList() {
if (!panelEl) return;
const list = panelEl.querySelector('.bl-list');
const count = panelEl.querySelector('.bl-count');
count.textContent = blocked.length;
if (!blocked.length) {
list.innerHTML = 'Список пуст';
} else {
list.innerHTML = '';
[...blocked].sort((a, b) => a.localeCompare(b, 'ru')).forEach(slug => {
const li = document.createElement('li');
const span = document.createElement('span');
span.textContent = slug;
const rm = document.createElement('button');
rm.textContent = 'удалить';
rm.addEventListener('click', () => removeAuthor(slug));
li.append(span, rm);
list.appendChild(li);
});
}
updateStats();
}
function updateStats() {
if (!panelEl) return;
const s = panelEl.querySelector('.bl-stats');
const tiles = document.querySelectorAll(TILE_SELECTOR);
let total = 0, visible = 0;
tiles.forEach(t => {
if (t.classList.contains('tile-banner')) return; // рекламные плитки
if (!t.querySelector(AUTHOR_LINK_SELECTOR)) return; // не карточка проекта
total++;
if (t.getClientRects().length > 0) visible++;
});
s.textContent = `Проектов: ${total} · видно: ${visible} · скрыто: ${total - visible}`;
}
function addAuthor(slug) {
slug = String(slug || '').trim().toLowerCase();
if (!slug) { showStatus('Пусто', 'error'); return; }
if (blocked.includes(slug)) { showStatus('Уже в списке: ' + slug); return; }
blocked.push(slug);
localStorage.setItem(STORAGE_KEY, JSON.stringify(blocked));
buildCss();
renderList();
showStatus('Добавлен: ' + slug);
}
function removeAuthor(slug) {
slug = slug.toLowerCase();
blocked = blocked.filter(x => x !== slug);
localStorage.setItem(STORAGE_KEY, JSON.stringify(blocked));
buildCss();
renderList();
showStatus('Удалён: ' + slug);
}
function init() {
panelEl = createPanel();
renderList();
buildCss();
// Обновление счётчика раз в 3 секунды, только на активной вкладке
setInterval(() => {
if (document.hidden) return;
updateStats();
}, 3000);
if (typeof GM_registerMenuCommand === 'function') {
GM_registerMenuCommand('Очистить ЧС авторов', () => {
if (!confirm('Очистить весь список?')) return;
blocked = [];
localStorage.setItem(STORAGE_KEY, '[]');
buildCss();
renderList();
});
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
Еще больше интересных статей
Держатель катушки филамента (с подтормаживанием)
Подпишитесь на автора
Подпишитесь на автора, если вам нравятся его публикации. Тогда вы будете получать уведомления о его новых статьях.
Отписаться от уведомлений вы всегда сможете в профиле автора.
SCANFORM готовит к серийному запуску флагманский 3D сканер
Подпишитесь на автора
Подпишитесь на автора, если вам нравятся его публикации. Тогда вы будете получать уведомления о его новых статьях.
Отписаться от уведомлений вы всегда сможете в профиле автора.
SCANFORM — российский производит...
Ультрабюджетный лазерный СО2 станок своими руками
Подпишитесь на автора
Подпишитесь на автора, если вам нравятся его публикации. Тогда вы будете получать уведомления о его новых статьях.
Отписаться от уведомлений вы всегда сможете в профиле автора.

Комментарии и вопросы
Стол будет ровный по углам в л...
Была технология армировки стек...
Это же дпо, а не 2 высшее, там...
Доброго времени суток всем, во...
Хочу перед хомингом отодвинуть...
Купил принтер flying bear ghos...
Давно уже было, вместо покупки...