KREMEN Реклама
KREMEN Реклама

Скрипт для фильтрации авторов на дэшборде моделей.

bront
Идет загрузка
Загрузка
05.10.2026
470
10
Разное

Подпишитесь на автора

Подпишитесь на автора, если вам нравятся его публикации. Тогда вы будете получать уведомления о его новых статьях.

Отписаться от уведомлений вы всегда сможете в профиле автора.

3

В последнее время стало очень много контента, что засоряет панель. На сайте родная фильтрация - примитивна. Надо с этим что-то делать. 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();

    }

})();

Подпишитесь на автора

Подпишитесь на автора, если вам нравятся его публикации. Тогда вы будете получать уведомления о его новых статьях.

Отписаться от уведомлений вы всегда сможете в профиле автора.

3
Комментарии к статье
Lider 3D Реклама
Lider 3D Реклама