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

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

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

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

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

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

0

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

    }

})();

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

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

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

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