Control Emotes Panel 2.6.52 (C) tapeavion

Twitch emoji blocking in Chat with a management interface and context menu .

// ==UserScript==
// @name          Control Emotes Panel 2.6.52 (C) tapeavion
// @version       2.6.52
// @description   Twitch emoji blocking in Chat with a management interface and context menu .
// @author        Gullampis810
// @license       Proprietary (c) tapeavion
// @match         https://www.twitch.tv/*
// @match         https://blank.org/*
// @grant         GM_registerMenuCommand
// @grant         GM_unregisterMenuCommand
// @grant         GM_setValue
// @grant         GM_getValue
// @connect       raw.githubusercontent.com
// @grant         GM_xmlhttpRequest
// @icon          https://raw.githubusercontent.com/sopernik566/icons/refs/heads/main/7bttvffzTtvlogo.ico
// @namespace     http://tampermonkey.net/
// @tag           twitch
// @tag           emote-blocker
// @tag           chat-filter
// @tag           block-emotes
// @tag           twitch-chat
// @tag           7tv
// @tag           bttv
// @tag           ffz
// @tag           twitch-channel
// @tag           block all emotes
// ==/UserScript==


(function () {
    'use strict';

    // Подключаем Chart.js через CDN
const chartJsScript = document.createElement('script');
chartJsScript.src = 'https://cdn.jsdelivr.net/npm/chart.js';
chartJsScript.async = true;
document.head.appendChild(chartJsScript);


    // === объект для хранения текущих стилей кнопки DeleteButton в случае пересоздания и сброса стиля //
    let currentDeleteButtonStyles = {
        background: 'rgb(168, 77, 77)', // Начальный цвет из hoverStyle
        color: '#fff',
        hoverBackground: 'linear-gradient(135deg, #f75557 0%, #480a0c 56%, #4e1314 98%, #ff4d4d 100%)'
    };

    let blockedEmotes = [];
    let blockedChannels = [];

        // глобальное определение для поисковой строки
    function debounce(func, wait) {
        let timeout;
        return function (...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    }

    // Функция для безопасного получения и парсинга данных
    function loadData(key, defaultValue) {
        const rawData = GM_getValue(key, defaultValue);
        try {
            return typeof rawData === 'string' ? JSON.parse(rawData) : rawData;
        } catch (e) {
            console.error(`Ошибка при парсинге ${key}:`, e);
            return defaultValue; // Возвращаем значение по умолчанию в случае ошибки
        }
    }

    // Загружаем данные при старте
    blockedEmotes = loadData("blockedEmotes", []);
    blockedChannels = loadData("blockedChannels", []);
    console.log("[7BTTVFZ Control Emotes Panel] Загружены blockedEmotes:", blockedEmotes);
    console.log("[7BTTVFZ Control Emotes Panel] Загружены blockedChannels:", blockedChannels);

    let isPanelOpen = GM_getValue('isPanelOpen', false);



//=== Функция для перемещения панели ===//
function makePanelDraggable(panel) {
    let offsetX = 0, offsetY = 0, isDragging = false;

    // Создаем заголовок, за который можно перетаскивать
    const dragHandle = document.createElement('div');
    dragHandle.style.width = '100%';
    dragHandle.style.height = '656px';
    dragHandle.style.background = 'rgba(0, 0, 0, 0.0)';
    dragHandle.style.cursor = 'grab';
    dragHandle.style.position = 'absolute';
    dragHandle.style.top = '0';
    dragHandle.style.left = '0';
    dragHandle.style.zIndex = '-1';
    dragHandle.style.borderRadius = '8px 8px 0 0';
    panel.appendChild(dragHandle);

    // Начало перемещения
    dragHandle.addEventListener('mousedown', (e) => {
        isDragging = true;
        offsetX = e.clientX - panel.getBoundingClientRect().left;
        offsetY = e.clientY - panel.getBoundingClientRect().top;
        dragHandle.style.cursor = 'grabbing';
    });

    // Перемещение панели
    document.addEventListener('mousemove', (e) => {
        if (!isDragging) return;
        panel.style.left = `${e.clientX - offsetX}px`;
        panel.style.top = `${e.clientY - offsetY}px`;
    });

    // Остановка перемещения
    document.addEventListener('mouseup', () => {
        isDragging = false;
        dragHandle.style.cursor = 'grab';
    });
}

//===================================== Панель управления =======================================//
    const controlPanel = document.createElement('div');
    controlPanel.style.position = 'fixed'; // Фиксируем панель на экране
    controlPanel.style.bottom = '124px'; // Располагаем панель на 124px от нижней границы экрана
    controlPanel.style.right = '380px'; // Располагаем панель на 310px от правой границы экрана
    controlPanel.style.width = '690px'; // Ширина панели
    controlPanel.style.height = '656px'; // Высота панели
    controlPanel.style.backgroundColor = '#5c5065'; // Цвет фона панели
    controlPanel.style.background = '-webkit-linear-gradient(270deg, hsla(50, 76%, 56%, 1) 0%, hsla(32, 83%, 49%, 1) 25%, hsla(0, 37%, 37%, 1) 59%, hsla(276, 47%,         24%, 1) 79%, hsla(261, 11%, 53%, 1) 100%)'; // Применяем градиентный фон

    controlPanel.style.border = '1px solid #ccc'; // Цвет и стиль границы панели
    controlPanel.style.borderRadius = '8px'; // Скругляем углы панели
    controlPanel.style.padding = '10px'; // Отступы внутри панели
    controlPanel.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)'; // Добавляем тень панели
    controlPanel.style.zIndex = 10000; // Устанавливаем высокий z-index, чтобы панель была поверх других элементов
    controlPanel.style.fontFamily = 'Arial, sans-serif'; // Шрифт текста на панели
    controlPanel.style.transition = 'height 0.3s ease'; // Плавное изменение высоты при изменении
    controlPanel.style.overflow = 'hidden'; // Скрытие содержимого, если оно выходит за пределы панели


        // Метка версии внизу панели
const versionLabel = document.createElement('div');
versionLabel.innerText = 'v.2.6.52';
versionLabel.style.position = 'absolute';
versionLabel.style.top = '4px';
versionLabel.style.right = '610px';
versionLabel.style.color = ' #3e2155';
versionLabel.style.fontSize = '12px';
versionLabel.style.fontFamily = 'Arial, sans-serif';
versionLabel.style.fontWeight = 'bold';
controlPanel.appendChild(versionLabel);


// Добавляем панель в DOM и активируем перетаскивание
document.body.appendChild(controlPanel);
makePanelDraggable(controlPanel);


// ======== Создаём кнопку шестерёнки для настроек ======== //
const settingsButton = document.createElement('button');
settingsButton.innerHTML = '<img src="https://raw.githubusercontent.com/sopernik566/Control_Emotes_Panel_Twitch_JS/dec21031f3f5d70d26b43c11ab4cb5b401b6c67d/settingsbttn7btttvttvffz.png" alt="Settings" style="width: 28px; height: 28px; vertical-align: middle; background: #5d5d5d; border-radius: 8px; border: 1px solid #333333;">';
settingsButton.style.width = '28px';
settingsButton.style.height = '28px';
settingsButton.style.position = 'absolute';
settingsButton.style.top = '3px';
settingsButton.style.right = '657px';
settingsButton.style.background = 'transparent';
settingsButton.style.border = 'none';
settingsButton.style.color = '#fff';
settingsButton.style.cursor = 'pointer';
settingsButton.style.zIndex = '10001';
settingsButton.title = getTranslation('settingsTitle');
controlPanel.appendChild(settingsButton);

// Добавляем CSS для попапа и анимации
const styleSheet = document.createElement('style');
styleSheet.textContent = `
    .settings-popup {
        position: fixed;
        top: 20%;
        left: 30%;
        width: 40%;
        height: 60%;
        background: linear-gradient(181deg, hsla(50, 76%, 56%, 1) 0%, hsla(0, 37%, 37%, 1) 59%, hsla(276, 47%, 24%, 1) 79%);
        border: 1px solid #bda3d7;
        border-radius: 12px;
        z-index: 10002;
        padding: 10px;
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
        box-shadow: 6px 12px 20px 9px rgb(0 0 0 / 74%);
        transform: translateY(-100%);
        opacity: 0;
    }

    .popup-enter {
        animation: slideDown 0.3s ease-in-out forwards;
    }

    .popup-exit {
        animation: slideUp 0.3s ease-in-out forwards;
    }

    @keyframes slideDown {
        from {
            transform: translateY(-100%);
            opacity: 0;
        }
        to {
            transform: translateY(0);
            opacity: 1;
        }
    }

    @keyframes slideUp {
        from {
            transform: translateY(0);
            opacity: 1;
        }
        to {
            transform: translateY(-100%);
            opacity: 0;
        }
    }

    .settings-popup .close-button {
        position: absolute;
        top: 8px;
        right: 8px;
        background: #2f213b;
        color: #fff;
        border: none;
        border-radius: 6px;
        padding: 6px 12px;
        cursor: pointer;
        font-size: 14px;
        transition: background 0.2s;
    }

    .settings-popup .settings-title {
        margin: 0 0 12px 0;
        font-size: 18px;
        color: #2f213b;
        font-weight: bold;
    }

    .settings-popup .settings-content {
        font-size: 14px;
        color: #fff;
    }

    .settings-popup .settings-description {
        margin: 0 0 12px 0;
        color: #2f213b;
    }

    .settings-popup .watchdog-section,
    .settings-popup .language-section,
    .settings-popup .font-size-section {
        margin-top: 15px;
    }

    .settings-popup .watchdog-label,
    .settings-popup .language-label,
    .settings-popup .font-size-label {
        display: block;
        margin-bottom: 6px;
        font-size: 14px;
        color: #2f213b;
    }

    .settings-popup .watchdog-interval,
    .settings-popup .language-select,
    .settings-popup .font-size-input {
        width: 125px;
        padding: 6px;
        border-radius: 6px;
        border: 1px solid #bda3d7;
        background: rgb(47, 33, 59);
        color: #bda3d7;
        font-size: 14px;
    }

    .settings-popup .language-select {
        width: 125px;
    }
`;
document.head.appendChild(styleSheet);

// Функция для получения переводов
function getTranslation(key, lang = GM_getValue('language', 'ru')) {
    const translations = {
        ru: {
            settingsFailed: 'Не удалось загрузить настройки',
            watchdogUpdated: 'Интервал watchdog обновлён до {interval}с',
            watchdogInvalid: 'Интервал должен быть минимум 1 секунда',
            languageUpdated: 'Язык интерфейса обновлён',
            closeButton: 'Закрыть',
            settingsTitle: 'Настройки',
            settingsDescription: 'Настройте параметры панели',
            watchdogLabel: 'Интервал проверки Watchdog (секунды):',
            languageLabel: 'Язык интерфейса:',
            blockedEmotesList: 'Список заблокированных эмодзи',
            platformLabel: 'Платформа',
            nameLabel: 'Имя',
            dateTimeLabel: 'Дата-Время',
            goToLastElement: 'Перейти к последнему элементу',
            showStatsChart: 'Показать статистику',
            deleteAll: 'Удалить все',
            export: 'Экспорт',
            import: 'Импорт',
            unblockAll: 'Разблокировать все эмодзи',
            blockAll: 'Заблокировать все эмодзи',
            searchPlaceholder: 'Поиск в списке заблокированных…',
            searchButton: 'Поиск',
            twitchChannelLabel: 'Twitch-канал',
            addChannelPlaceholder: 'Введите канал для добавления',
            addChannelButton: 'Добавить'
        },
        en: {
            settingsFailed: 'Failed to load settings',
            watchdogUpdated: 'Watchdog interval updated to {interval}s',
            watchdogInvalid: 'Interval must be at least 1 second',
            languageUpdated: 'Interface language updated',
            closeButton: 'Close',
            settingsTitle: 'Settings',
            settingsDescription: 'Configure panel settings',
            watchdogLabel: 'Watchdog check interval (seconds):',
            languageLabel: 'Interface language:',
            blockedEmotesList: 'List of Blocked Emotes',
            platformLabel: 'Platform',
            nameLabel: 'Name',
            dateTimeLabel: 'Date-Time',
            goToLastElement: 'Go to Last Element',
            showStatsChart: 'Show Stats Chart',
            deleteAll: 'Delete All',
            export: 'Export',
            import: 'Import',
            unblockAll: 'Unblock All Emotes',
            blockAll: 'Block All Emotes',
            searchPlaceholder: 'Search in blocked list…',
            searchButton: 'Search',
            twitchChannelLabel: 'Twitch Channel',
            addChannelPlaceholder: 'Type to add channel',
            addChannelButton: 'Add it'
        }
    };
    return translations[lang][key] || translations.ru[key];
}

// Функция для обновления переводов панели
function updatePanelTranslations(lang = GM_getValue('language', 'ru')) {
    const blockedEmotesList = document.querySelector('.blocked-emotes-list');
    if (blockedEmotesList) {
        blockedEmotesList.textContent = getTranslation('blockedEmotesList', lang);
    }

    const platformLabel = document.querySelector('.platform-label');
    if (platformLabel) {
        platformLabel.textContent = getTranslation('platformLabel', lang);
    }

    const nameLabel = document.querySelector('.name-label');
    if (nameLabel) {
        nameLabel.textContent = getTranslation('nameLabel', lang);
    }

    const dateTimeLabel = document.querySelector('.date-time-label');
    if (dateTimeLabel) {
        dateTimeLabel.textContent = getTranslation('dateTimeLabel', lang);
    }

    const goToLastElement = document.querySelector('.go-to-last-element');
    if (goToLastElement) {
        goToLastElement.textContent = getTranslation('goToLastElement', lang);
    }

    const showStatsChart = document.querySelector('.show-stats-chart');
    if (showStatsChart) {
        showStatsChart.textContent = getTranslation('showStatsChart', lang);
    }

    const deleteAll = document.querySelector('.delete-all');
    if (deleteAll) {
        deleteAll.textContent = getTranslation('deleteAll', lang);
    }

    const exportButton = document.querySelector('.export-button');
    if (exportButton) {
        exportButton.textContent = getTranslation('export', lang);
    }

    const importButton = document.querySelector('.import-button');
    if (importButton) {
        importButton.textContent = getTranslation('import', lang);
    }

    const unblockAll = document.querySelector('.unblock-all');
    if (unblockAll) {
        unblockAll.textContent = getTranslation('unblockAll', lang);
    }

    const blockAll = document.querySelector('.block-all');
    if (blockAll) {
        blockAll.textContent = getTranslation('blockAll', lang);
    }

    const searchInput = document.querySelector('.search-input');
    if (searchInput) {
        searchInput.placeholder = getTranslation('searchPlaceholder', lang);
    }

    const searchButton = document.querySelector('.search-button');
    if (searchButton) {
        searchButton.textContent = getTranslation('searchButton', lang);
    }

    const twitchChannelLabel = document.querySelector('.twitch-channel-label');
    if (twitchChannelLabel) {
        twitchChannelLabel.textContent = getTranslation('twitchChannelLabel', lang);
    }

    const addChannelInput = document.querySelector('.add-channel-input');
    if (addChannelInput) {
        addChannelInput.placeholder = getTranslation('addChannelPlaceholder', lang);
    }

    const addChannelButton = document.querySelector('.add-channel-button');
    if (addChannelButton) {
        addChannelButton.textContent = getTranslation('addChannelButton', lang);
    }

    settingsButton.title = getTranslation('settingsTitle', lang);
}

// Инициализация переводов панели при загрузке
const scriptVersion = '2.6.52';
updatePanelTranslations();

// Обработчик клика по кнопке настроек
settingsButton.onclick = () => {
    if (document.querySelector('.settings-popup')) {
        return;
    }

    GM_xmlhttpRequest({
        method: 'GET',
        url: 'https://raw.githubusercontent.com/sopernik566/Control_Emotes_Panel_Twitch_JS/refs/heads/main/settingsPopup.html',
        onload: function(response) {
            try {
                const popup = document.createElement('div');
                popup.innerHTML = response.responseText;
                // Убедимся, что класс .settings-popup добавлен только один раз
                const popupContainer = popup.querySelector('.settings-popup') || popup;
                popupContainer.className = 'settings-popup';
                document.body.appendChild(popupContainer);

                // Запускаем анимацию открытия
                requestAnimationFrame(() => {
                    popupContainer.classList.add('popup-enter');
                });

                const lang = GM_getValue('language', 'ru');
                const closeButton = popupContainer.querySelector('.close-button');
                if (closeButton) {
                    closeButton.textContent = getTranslation('closeButton', lang);
                    closeButton.onclick = () => {
                        popupContainer.classList.remove('popup-enter');
                        popupContainer.classList.add('popup-exit');
                        popupContainer.addEventListener('animationend', () => {
                            popupContainer.remove();
                        }, { once: true });
                    };
                    closeButton.onmouseover = () => {
                        closeButton.style.background = '#a88cc7';
                    };
                    closeButton.onmouseout = () => {
                        closeButton.style.background = '#2f213b';
                    };
                } else {
                    console.error('[7BTTVFZ Control Emotes Panel] Кнопка закрытия не найдена');
                }

                const settingsTitle = popupContainer.querySelector('.settings-title');
                if (settingsTitle) {
                    settingsTitle.textContent = getTranslation('settingsTitle', lang);
                }

                const settingsDescription = popupContainer.querySelector('.settings-description');
                if (settingsDescription) {
                    settingsDescription.textContent = getTranslation('settingsDescription', lang);
                }

                const watchdogLabel = popupContainer.querySelector('.watchdog-label');
                if (watchdogLabel) {
                    watchdogLabel.textContent = getTranslation('watchdogLabel', lang);
                }

                const languageLabel = popupContainer.querySelector('.language-label');
                if (languageLabel) {
                    languageLabel.textContent = getTranslation('languageLabel', lang);
                }

                const watchdogInput = popupContainer.querySelector('.watchdog-interval');
                if (watchdogInput) {
                    watchdogInput.value = GM_getValue('watchdogInterval', 10);
                    watchdogInput.onchange = () => {
                        const interval = parseInt(watchdogInput.value, 10);
                        if (interval >= 1) {
                            GM_setValue('watchdogInterval', interval);
                            console.log(`[7BTTVFZ Control Emotes Panel] Интервал watchdog установлен: ${interval} сек`);
                            showNotification(getTranslation('watchdogUpdated', lang).replace('{interval}', interval), 3000);
                        } else {
                            showNotification(getTranslation('watchdogInvalid', lang), 3000);
                            console.log('[7BTTVFZ Control Emotes Panel] Неверный интервал watchdog');
                        }
                    };
                }

                const languageSelect = popupContainer.querySelector('.language-select');
                if (languageSelect) {
                    languageSelect.value = GM_getValue('language', 'ru');
                    languageSelect.onchange = () => {
                        const newLang = languageSelect.value;
                        GM_setValue('language', newLang);
                        console.log(`[7BTTVFZ Control Emotes Panel] Язык интерфейса изменён на: ${newLang}`);
                        showNotification(getTranslation('languageUpdated', newLang), 3000);
                        if (closeButton) closeButton.textContent = getTranslation('closeButton', newLang);
                        if (settingsTitle) settingsTitle.textContent = getTranslation('settingsTitle', newLang);
                        if (settingsDescription) settingsDescription.textContent = getTranslation('settingsDescription', newLang);
                        if (watchdogLabel) watchdogLabel.textContent = getTranslation('watchdogLabel', newLang);
                        if (languageLabel) languageLabel.textContent = getTranslation('languageLabel', newLang);
                        updatePanelTranslations(newLang);
                    };
                }

                // Поддержка настройки размера шрифта (если активирована)
                const fontSizeInput = popupContainer.querySelector('.font-size-input');
                if (fontSizeInput) {
                    fontSizeInput.value = GM_getValue('buttonFontSize', 16);
                    fontSizeInput.onchange = () => {
                        const size = parseInt(fontSizeInput.value, 10);
                        if (size >= 1 && size <= 35) {
                            GM_setValue('buttonFontSize', size);
                            console.log(`[7BTTVFZ Control Emotes Panel] Размер шрифта установлен: ${size}px`);
                            showNotification(`Font size updated to ${size}px`, 3000);
                        } else {
                            showNotification('Font size must be between 1 and 35', 3000);
                            console.log('[7BTTVFZ Control Emotes Panel] Неверный размер шрифта');
                        }
                    };
                }

                console.log('[7BTTVFZ Control Emotes Panel] Попап настроек загружен с GitHub');
            } catch (err) {
                console.error('[7BTTVFZ Control Emotes Panel] Ошибка загрузки попапа:', err);
                showNotification(getTranslation('settingsFailed', lang), 3000);
                createFallbackSettingsPopup();
            }
        },
        onerror: function(err) {
            console.error('[7BTTVFZ Control Emotes Panel] Ошибка загрузки с GitHub:', err);
            showNotification(getTranslation('settingsFailed', GM_getValue('language', 'ru')), 3000);
            createFallbackSettingsPopup();
        }
    });
};

// Резервный попап на случай сбоя
function createFallbackSettingsPopup() {
    const settingsPopup = document.createElement('div');
    settingsPopup.className = 'settings-popup';
    const lang = GM_getValue('language', 'ru');

    const closeButton = document.createElement('button');
    closeButton.className = 'close-button';
    closeButton.textContent = getTranslation('closeButton', lang);
    closeButton.onclick = () => {
        settingsPopup.classList.remove('popup-enter');
        settingsPopup.classList.add('popup-exit');
        settingsPopup.addEventListener('animationend', () => {
            settingsPopup.remove();
        }, { once: true });
    };
    closeButton.onmouseover = () => {
        closeButton.style.background = '#a88cc7';
    };
    closeButton.onmouseout = () => {
        closeButton.style.background = '#2f213b';
    };

    settingsPopup.appendChild(closeButton);
    settingsPopup.innerHTML += `
        <h3 class="settings-title">${getTranslation('settingsTitle', lang)}</h3>
        <div class="settings-content">
            <p class="settings-description">${getTranslation('settingsFailed', lang)}</p>
        </div>
    `;
    document.body.appendChild(settingsPopup);

    // Запускаем анимацию открытия
    requestAnimationFrame(() => {
        settingsPopup.classList.add('popup-enter');
    });
}
// ================ конец end of popupsettings ==================== //




//---------------Текст title  "Название"  "лист" список list of BlockedEmotes ------------------------//
const title = document.createElement('h4');
title.innerText = 'list of BlockedEmotes';
title.style.margin = '-5px 0px 10px'; // Отступы сверху и снизу
title.style.color = ' #2a1e38'; // Обновленный цвет
title.style.position = 'relative'; // Устанавливаем позицию относительно
title.style.bottom = '35px'; // Сдвиг по вертикали
title.style.width = '190px';
controlPanel.appendChild(title);



//--------------- Список заблокированных каналов ------------------//
const list = document.createElement('ul');
list.id = 'blockedList';
list.style.position = 'relative';
list.style.bottom = '34px';
list.style.border = '1px solid #ffffff'; // Белая граница
list.style.borderRadius = '0px 0px 8px 8px'; // Скругление углов
list.style.boxShadow = ' rgb(0 0 0 / 67%) -18px 69px 40px 0 inset '; // Вставка тени в контейнер
list.style.listStyle = 'none'; // Убираем стандартные маркеры списка
list.style.padding = '0'; // Убираем отступы
list.style.margin = '-14px 0px 10px'; // Отступ снизу
list.style.maxHeight = '570px'; // Устанавливаем максимальную высоту
list.style.height = '410px'; // Высота списка
list.style.overflowY = 'auto'; // Включаем вертикальную прокрутку


//==================================== ГРАДИЕНТ ФОН СПИСОК =================================================//
// Добавляем линейный градиент фона с кроссбраузерностью
list.style.background = 'linear-gradient(45deg, hsla(292, 44%, 16%, 1) 0%, hsla(173, 29%, 48%, 1) 100%)';
list.style.background = '-moz-linear-gradient(45deg, hsla(292, 44%, 16%, 1) 0%, hsla(173, 29%, 48%, 1) 100%)'; // Для Firefox
list.style.background = '-webkit-linear-gradient(45deg, hsla(292, 44%, 16%, 1) 0%, hsla(173, 29%, 48%, 1) 100%)'; // Для Safari и Chrome
list.style.filter = 'progid: DXImageTransform.Microsoft.gradient(startColorstr="#36173b", endColorstr="#589F97", GradientType=1)'; // Для старых версий IE
list.style.color = '#fff'; // Белый цвет текста

//==========  кастомный scroll bar для списка =============//
const style = document.createElement('style');
style.innerHTML = `
#blockedList::-webkit-scrollbar {
  width: 25px; /* Ширина скроллбара */
}

#blockedList::-webkit-scrollbar-thumb {
  background-color: #C1A5EF; /* Цвет бегунка */
  border-radius: 8px; /* Скругление бегунка */
  border: 3px solid #4F3E6A; /* Внутренний отступ (цвет трека) */
  height: 80px; /* Высота бегунка */
}

#blockedList::-webkit-scrollbar-thumb:hover {
  background-color: #C6AEFF; /* Цвет бегунка при наведении */
}

#blockedList::-webkit-scrollbar-thumb:active {
  background-color: #B097C9; /* Цвет бегунка при активном состоянии */
}

#blockedList::-webkit-scrollbar-track {
  background: #455565; /* Цвет трека */
  border-radius: 0px 0px 8px 0px; /* Скругление только нижнего правого угла */
}

#blockedList::-webkit-scrollbar-track:hover {
  background-color: #455565; /* Цвет трека при наведении */
}

#blockedList::-webkit-scrollbar-track:active {
  background-color: #455565; /* Цвет трека при активном состоянии */
}

`;
document.head.appendChild(style);

// hover blocked-item элемент списка //
const hoverStyle = document.createElement('style');
hoverStyle.innerHTML = `
    .blocked-item {
        transition: background-color 0.3s ease, color 0.3s ease;
    }
    .blocked-item:hover {
        background-color: rgba(167, 54, 54, 0.52);
        color: #42d13a;
    }
    .blocked-item:hover span {
        color: #42d13a;
    }
    .new-item {
        background-color:#28a828;
        transition: background-color 0.3s ease;
    }
    .new-item:hover {
        background-color: #3a2252;
        color: #af7fcf;
    }
    .new-item:hover span {
        color: #af7fcf;
    }
    #sortContainer button {
        background: none;
        border: none;
        color: inherit;
        font-family: inherit;
        font-size: inherit;
        padding: 0 10px;
        transition: color 0.3s ease;
    }
    #sortContainer button:hover {
        color: #9ae048;
    }
`;
document.head.appendChild(hoverStyle);



const highlightStyle = document.createElement('style');
highlightStyle.innerHTML = `
    .blocked-item .highlight {
        background-color: #FFEB3B  !important; /* Красный фон для подсветки */
        color:rgb(0, 0, 0) !important; /* Белый текст для контраста */
        padding: 0 2px !important;
        border-radius: 2px !important;
        transition: background-color 0.5s ease !important;
    }
    .blocked-item.highlight-item {
        background-color: rgba(163, 161, 18, 0.83) !important; /* Полупрозрачная красная подсветка для всего элемента */
        transition: background-color 0.5s ease !important;
    }
     .last-item-highlight {
          background-color: #279479; /* Полупрозрачный золотой фон */
          transition: background-color 0.5s ease; /* Плавное исчезновение */
}
`;
document.head.appendChild(highlightStyle);

document.head.appendChild(style);

const buttonColor = ' #907cad'; // Общий цвет для кнопок
const buttonShadow = '0 4px 8px rgba(0, 0, 0, 0.6)'; // Тень для кнопок (60% прозрачности)



// Функция для обновления списка заблокированных каналов

// Переменные для хранения ID заблокированных элементов
let blockedEmoteIDs = new Set();
let blockedChannelIDs = new Set();
let newlyAddedIds = new Set();

function updateBlockedList() {
    list.innerHTML = '';

    // Очистка и обновление Set для быстрого поиска ID
    blockedEmoteIDs.clear();
    blockedChannelIDs.clear();

    function createListItem(channel, isNew = false) {
        const item = document.createElement('li');
        item.className = 'blocked-item';
        item.dataset.id = channel.id;

        if (isNew) {
            item.classList.add('new-item');
            setTimeout(() => {
                item.classList.remove('new-item');
            }, 1800000);
        }

        item.style.display = 'flex';
        item.style.flexDirection = 'column';
        item.style.padding = '5px';
        item.style.borderBottom = '1px solid #eee';

        const topRow = document.createElement('div');
        topRow.style.display = 'flex';
        topRow.style.justifyContent = 'space-between';
        topRow.style.alignItems = 'center';

        const channelName = document.createElement('span');
        if (channel.platform === 'TwitchChannel') {
            channelName.innerText = `${channel.platform} > name emote: ${channel.emoteName}`;
        } else {
            channelName.innerText = `${channel.platform} > ${channel.emoteName}`;
        }
        channelName.classList.add('list-item-text');
        channelName.style.flex = '1';
        channelName.style.fontSize = '14px';
        channelName.style.fontWeight = 'bold';
        channelName.style.whiteSpace = 'nowrap';
        channelName.style.overflow = 'hidden';
        channelName.style.textOverflow = 'ellipsis';
        topRow.appendChild(channelName);

        const dateInfo = document.createElement('span');
        const date = new Date(channel.date);
        dateInfo.innerText = isNaN(date.getTime())
            ? 'Unknown Date'
            : date.toLocaleString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' });
        dateInfo.classList.add('list-item-date');
        dateInfo.style.marginRight = '30px';
        dateInfo.style.fontSize = '14px';
        topRow.appendChild(dateInfo);

        const removeButton = document.createElement('button');
        removeButton.innerText = 'Delete';
        removeButton.classList.add('delete-button');
        console.log("[7BTTVFZ Control Emotes Panel] Создана кнопка Delete для элемента:", channel.id);

        // Используем сохранённые стили из currentDeleteButtonStyles
        removeButton.style.background = currentDeleteButtonStyles.background;
        removeButton.style.color = currentDeleteButtonStyles.color;
        removeButton.style.height = '35px';
        removeButton.style.width = '75px';
        removeButton.style.fontWeight = 'bold';
        removeButton.style.fontSize = '16px';
        removeButton.style.border = 'none';
        removeButton.style.borderRadius = '4px';
        removeButton.style.cursor = 'pointer';
        removeButton.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.6)';
        removeButton.style.display = 'flex';
        removeButton.style.alignItems = 'center';
        removeButton.style.justifyContent = 'center';
        removeButton.style.transition = 'background 0.3s ease';
        removeButton.style.opacity = '1';
        removeButton.style.visibility = 'visible';

        // Добавляем обработчики наведения
        removeButton.onmouseover = () => {
            removeButton.style.background = currentDeleteButtonStyles.hoverBackground;
        };
        removeButton.onmouseout = () => {
            removeButton.style.background = currentDeleteButtonStyles.background;
        };

        removeButton.onclick = function () {
            if (channel.platform === 'TwitchChannel') {
                blockedChannels = blockedChannels.filter(c => c.id !== channel.id);
                blockedChannelIDs.delete(channel.id);
                GM_setValue("blockedChannels", JSON.stringify(blockedChannels, null, 2));
            } else {
                blockedEmotes = blockedEmotes.filter(c => c.id !== channel.id);
                blockedEmoteIDs.delete(channel.id);
                GM_setValue("blockedEmotes", JSON.stringify(blockedEmotes, null, 2));
            }

            newlyAddedIds.delete(channel.id);
            updateBlockedList();
            updateCounter();
            showEmoteForChannel(channel);
        };

        topRow.appendChild(removeButton);
        item.appendChild(topRow);

        const channelLink = document.createElement('span');
        channelLink.innerText = `(prefix: ${channel.name})`;
        channelLink.classList.add('list-item-link');
        channelLink.style.fontSize = '14px';
        channelLink.style.wordBreak = 'break-word';
        channelLink.style.marginTop = '1px';
        item.appendChild(channelLink);

        return item;
    }

    // Заполняем списки и обновляем Set
    blockedChannels.forEach(channel => {
        blockedChannelIDs.add(channel.id);
        const isNew = newlyAddedIds.has(channel.id) && Array.from(newlyAddedIds).pop() === channel.id;
        list.appendChild(createListItem(channel, isNew));
    });

    blockedEmotes.forEach(channel => {
        blockedEmoteIDs.add(channel.id);
        const isNew = newlyAddedIds.has(channel.id) && Array.from(newlyAddedIds).pop() === channel.id;
        list.appendChild(createListItem(channel, isNew));
    });

    // Прокручиваем к последнему добавленному элементу внутри контейнера list
    if (newlyAddedIds.size > 0) {
        const lastAddedId = Array.from(newlyAddedIds).pop();
        const newItem = list.querySelector(`[data-id="${lastAddedId}"]`);
        if (newItem) {
            // Вычисляем позицию нового элемента относительно контейнера list
            const itemOffsetTop = newItem.offsetTop; // Позиция элемента относительно начала списка
            const listHeight = list.clientHeight; // Видимая высота контейнера list
            const itemHeight = newItem.clientHeight; // Высота самого элемента

            // Вычисляем, куда нужно прокрутить, чтобы элемент оказался вверху видимой области
            const scrollPosition = itemOffsetTop - (listHeight / 2) + (itemHeight / 2);

            // Плавно прокручиваем list к нужной позиции
            list.scrollTo({
                top: scrollPosition,
                behavior: 'smooth'
            });
        }
    }

    // Очищаем список новых ID после отображения
    newlyAddedIds.clear();
}

// Добавляем список в панель управления
controlPanel.appendChild(list);




// Создаём контейнер для поисковой строки
const searchContainer = document.createElement('div');
searchContainer.style.display = 'flex';
searchContainer.style.gap = '5px';
searchContainer.style.top = '500px';
searchContainer.style.position = 'relative';

// Создаём поисковую строку
const searchInput = document.createElement('input');
searchInput.type = 'text';
searchInput.placeholder = 'Search in blocked list...';
searchInput.style.background = ' #192427';
searchInput.style.width = '459px';
searchInput.style.left = '132px';
searchInput.style.color = ' #b69dcf';
searchInput.style.fontWeight = 'bold';
searchInput.style.height = '35px';
searchInput.style.padding = '5px';
searchInput.style.border = '1px solid #b69dcf';
searchInput.style.borderRadius = '4px';
searchInput.style.boxShadow = ' #4c2a5e 0px 4px 6px inset';
searchInput.style.position = 'relative';
searchInput.style.bottom = '50px';

// Создаём кнопку поиска
const searchButton = document.createElement('button');
searchButton.innerText = 'Search'; // Меняем текст на "Search"
searchButton.style.background = buttonColor;
searchButton.style.position = 'relative';
searchButton.style.bottom = '50px';
searchButton.style.color = '#fff';
searchButton.style.border = 'none';
searchButton.style.width = '72px';
 searchButton.style.left = '132px';
searchButton.style.borderRadius = '4px';
searchButton.style.padding = '5px 10px';
searchButton.style.cursor = 'pointer';
searchButton.style.fontSize = '16px';
searchButton.style.fontWeight = 'bold';
searchButton.style.boxShadow = buttonShadow;

// Добавляем ховер-эффекты для кнопки поиска
searchButton.onmouseover = function() {
    searchButton.style.background = '-webkit-linear-gradient(135deg, #443157 0%,rgb(90, 69, 122) 56%, #443157 98%, #443157 100%)';
};
searchButton.onmouseout = function() {
    searchButton.style.background = buttonColor;
};

// Обработчик кнопки поиска
searchButton.onclick = () => {
    const searchTerm = searchInput.value.trim();
    filterBlockedList(searchTerm); // Запускаем фильтрацию
};

function filterBlockedList(searchTerm) {
    const lowerSearchTerm = searchTerm.toLowerCase().trim();
    console.log("[7BTTVFZ Control Emotes Panel] Поисковый запрос (lowerSearchTerm):", lowerSearchTerm);

    let filteredList = [];

    // Фильтрация списка
    if (!lowerSearchTerm) {
        filteredList = [...blockedChannels, ...blockedEmotes];
        console.log("[7BTTVFZ Control Emotes Panel] Поиск пустой, отображаем все элементы:", filteredList);
    } else {
        filteredList = [...blockedChannels, ...blockedEmotes].filter(item => {
            const emoteName = item.emoteName || '';
            const platform = item.platform || '';
            const name = item.name || '';
            const matches =
                emoteName.toLowerCase().includes(lowerSearchTerm) ||
                platform.toLowerCase().includes(lowerSearchTerm) ||
                name.toLowerCase().includes(lowerSearchTerm);
            console.log(`[7BTTVFZ Control Emotes Panel] Проверяем элемент: ${JSON.stringify(item)}, совпадение: ${matches}`);
            return matches;
        });
        console.log("[7BTTVFZ Control Emotes Panel] Результаты фильтрации:", filteredList);
    }

    // Сохраняем текущую позицию скролла
    const currentScrollPosition = list.scrollTop;
    console.log("[7BTTVFZ Control Emotes Panel] Текущая позиция скролла перед обновлением:", currentScrollPosition);

    // Получаем текущие элементы в DOM
    const currentItems = list.querySelectorAll('.blocked-item');
    const existingIds = new Set([...currentItems].map(item => item.dataset.id));

    // Удаляем элементы, которые не прошли фильтрацию
    currentItems.forEach(item => {
        const itemId = item.dataset.id;
        if (!filteredList.some(f => f.id === itemId)) {
            item.remove();
        }
    });

    // Добавляем или обновляем элементы
    filteredList.forEach(channel => {
        const itemId = channel.id;
        let item = list.querySelector(`[data-id="${itemId}"]`);

        if (!item) {
            // Если элемента нет, создаём новый
            item = createListItem(channel);
            list.appendChild(item);
        }

        // Применяем подсветку, если есть поисковый запрос
        if (lowerSearchTerm) {
            const spans = item.querySelectorAll('span');
            spans.forEach(span => {
                const originalText = span.textContent || '';
                if (originalText.toLowerCase().includes(lowerSearchTerm)) {
                    const regex = new RegExp(`(${lowerSearchTerm})`, 'gi');
                    const highlightedText = originalText.replace(regex, '<span class="highlight">$1</span>');
                    span.innerHTML = highlightedText;
                }
            });
        }
    });

    // Прокрутка к первому элементу
    if (filteredList.length > 0) {
        const firstItem = list.querySelector('.blocked-item');
        if (firstItem) {
            console.log("[7BTTVFZ Control Emotes Panel] Найден первый элемент для прокрутки:", firstItem);
            const firstItemOffset = firstItem.offsetTop;
            list.scrollTop = firstItemOffset - (list.clientHeight / 2) + (firstItem.clientHeight / 2);
            console.log("[7BTTVFZ Control Emotes Panel] Установлен scrollTop:", list.scrollTop);
        } else {
            console.log("[7BTTVFZ Control Emotes Panel] Первый элемент не найден в DOM!");
        }
    } else {
        // Если список пуст, восстанавливаем скролл
        console.log("[7BTTVFZ Control Emotes Panel] Список пуст, восстанавливаем скролл на:", currentScrollPosition);
        list.scrollTop = currentScrollPosition;
    }

    updateCounter();
}

// Добавляем элементы в контейнер поиска
searchContainer.appendChild(searchInput);
searchContainer.appendChild(searchButton); //   searchButton


// Добавляем контейнер поиска в панель управления
controlPanel.appendChild(searchContainer);

// Далее продолжаем с добавлением списка
controlPanel.appendChild(list);


//================= Функционал для добавления нового канала в список заблокированных ==================//
const inputContainer = document.createElement('div');
inputContainer.style.display = 'flex';
inputContainer.style.gap = '5px';

const input = document.createElement('input');
input.type = 'text';
input.placeholder = 'type to add channel ';
input.style.position = 'relative';
input.style.background = ' #192427';
input.style.color = ' #b69dcf';
input.style.flex = '1';
input.style.fontWeight = 'bold'; // Жирный текст
input.style.height = '35px'; // Отступ между кнопкой и поисковой строкой
input.style.padding = '5px';
input.style.border = '1px solid #b69dcf';
input.style.borderRadius = '4px';
input.style.top = '15px'; // Отступ между кнопкой и поисковой строкой
// Добавление тени с фиолетовым цветом (35% прозрачности) внутрь
input.style.boxShadow = ' #4c2a5e 0px 4px 6px inset'; // Тень фиолетового цвета внутри

//================== Add it Button =====================//
// ==================== Кнопка добавления ===================== //
const addButton = document.createElement('button');
addButton.innerText = 'Add it';
addButton.style.background = buttonColor;
addButton.style.top = '15px'; // Отступ между кнопкой и поисковой строкой
addButton.style.position = 'relative';
addButton.style.color = '#fff';
addButton.style.border = 'none';
addButton.style.width = '72px';
addButton.style.borderRadius = '4px';
addButton.style.padding = '5px 10px';
addButton.style.cursor = 'pointer';
addButton.style.boxShadow = buttonShadow; // Тень для кнопки "Add it"

// Увеличиваем размер текста и делаем его жирным
addButton.style.fontSize = '16px'; // Увеличиваем размер текста
addButton.style.fontWeight = 'bold'; // Жирный текст

// Генерация уникального ID
function generateID() {
    return `emote_${Date.now()}`; // Генерация ID на основе времени
}

addButton.onclick = (event) => {
    event.preventDefault();
    const channel = input.value.trim();
    const platform = platformSelect.value;

    if (channel) {
        let emoteName = channel;
        let emoteUrl = channel;
        const emoteId = generateRandomID();

        // Проверка на дублирование
        const isDuplicate = platform === 'TwitchChannel'
            ? blockedChannels.some(e => e.name === channel && e.platform === platform)
            : blockedEmotes.some(e => e.emoteUrl === channel && e.platform === platform);

        if (isDuplicate) {
            console.log(`%c[7BTTVFZ Control Emotes Panel] %cChannel/Emote already blocked: ${channel}`,
                'color: rgb(255, 165, 0); font-weight: bold;',
                'color: rgb(255, 165, 0);');
            return;
        }

        if (platform === '7tv' || platform === 'bttTV' || platform === 'ffz') {
            const img = document.querySelector(`img[src="${channel}"]`);
            if (img) {
                emoteName = img.alt || channel.split('/').pop();
                emoteUrl = img.src || channel;
            }

            const newEmote = {
                id: emoteId,
                name: emoteUrl,
                platform: platform,
                emoteName: emoteName,
                emoteUrl: emoteUrl,
                date: new Date().toISOString()
            };

            blockedEmotes.push(newEmote);
            blockedEmoteIDs.add(emoteId);
            newlyAddedIds.add(emoteId);
            GM_setValue("blockedEmotes", JSON.stringify(blockedEmotes, null, 2));
            console.log(`%c[7BTTVFZ Control Emotes Panel] %cAdded to blockedEmotes:`,
                'color: rgb(0, 255, 0); font-weight: bold;',
                'color: rgb(0, 255, 0);', newEmote);
        } else if (platform === 'TwitchChannel') {
            const prefix = channel.split(/[^a-zA-Z0-9]/)[0];
            emoteUrl = prefix;

            const newChannel = {
                id: emoteId,
                name: emoteUrl,
                platform: platform,
                emoteName: emoteName,
                emoteUrl: emoteUrl,
                date: new Date().toISOString()
            };

            blockedChannels.push(newChannel);
            blockedChannelIDs.add(emoteId);
            newlyAddedIds.add(emoteId);
            GM_setValue("blockedChannels", JSON.stringify(blockedChannels, null, 2));
            console.log(`%c[7BTTVFZ Control Emotes Panel] %cAdded to blockedChannels:`,
                'color: rgb(0, 255, 0); font-weight: bold;',
                'color: rgb(0, 255, 0);', newChannel);
        }

        const chatContainer = document.querySelector('.chat-scrollable-area__message-container');
        if (chatContainer) {
            toggleEmotesInNode(chatContainer);
        }

        updateBlockedList();
        updateCounter();
        input.value = '';
    }
};



// ==================== Создание выпадающего списка платформ ===================== //
const platformSelect = document.createElement('select');
platformSelect.style.top = '15px'; // Отступ между кнопкой и поисковой строкой
platformSelect.style.position = 'relative';
platformSelect.style.height = '35px'; // Высота выпадающего списка
platformSelect.style.border = '1px solid #c1a5ef';
platformSelect.style.background = '#192427';
platformSelect.style.borderRadius = '4px';
platformSelect.style.padding = '5px';
platformSelect.style.fontWeight = 'bold'; // Жирный текст
platformSelect.style.color = ' #b69dcf';

const platforms = ['TwitchChannel', '7tv', 'bttTV', 'ffz'];
platforms.forEach(platform => {
    const option = document.createElement('option');
    option.value = platform;
    option.innerText = platform;
    platformSelect.appendChild(option);
});

// ==================== Подсказки для выбора платформы ===================== //
platformSelect.addEventListener('change', () => {
    const placeholderText = {
        'TwitchChannel': 'example prefix abcd123',
        '7tv': 'link example: https://cdn.7tv.app/emote/00000000000000000000000000/2x.webp',
        'bttTV': 'link example: https://cdn.betterttv.net/emote/000000000000000000000000/2x.webp',
        'ffz': 'link example: https://cdn.frankerfacez.com/emote/0000/2'
    };
    input.placeholder = placeholderText[platformSelect.value];
});

// ==================== Добавление выпадающего списка в контейнер ===================== //
inputContainer.appendChild(platformSelect);



//----------------Единый контейнер для кнопок -------------------------//
const buttonContainer = document.createElement('div');
buttonContainer.style.display = 'flex'; // Используем flexbox для расположения кнопок в строку
buttonContainer.style.alignItems = 'baseline'; // Используем flexbox для расположения кнопок в строку
buttonContainer.style.alignContent = 'stretch';



buttonContainer.style.gap = '13px'; // Задаем промежуток между кнопками
buttonContainer.style.bottom = '113px'; // Отступ сверху для контейнера кнопок
buttonContainer.style.position = 'relative'; // Позиционирование относительно
buttonContainer.style.fontWeight = 'bold'; // Жирный текст для контейнера кнопок
buttonContainer.style.fontSize = '16px'; // Размер шрифта для кнопок
buttonContainer.style.width = '668px'; // Ширина кнопок (увеличена для эффекта растяжения




//-------------- Кнопка "Delete all" ------------------------//
const clearAllButton = document.createElement('button');
clearAllButton.innerText = 'Delete all'; // Текст на кнопке
clearAllButton.style.background = buttonColor; // Цвет фона кнопки
clearAllButton.style.color = '#fff'; // Цвет текста кнопки
clearAllButton.style.border = 'none'; // Убираем бордер у кнопки
clearAllButton.style.borderRadius = '4px'; // Скругленные углы кнопки
clearAllButton.style.padding = '5px 10px'; // Отступы внутри кнопки
clearAllButton.style.cursor = 'pointer'; // Курсор в виде руки при наведении
clearAllButton.style.boxShadow = buttonShadow; // Тень для кнопки "Delete all"

buttonContainer.appendChild(clearAllButton); // Добавляем кнопку в контейнер

// Обработчик события для кнопки "Delete all"
clearAllButton.onclick = () => {
    blockedEmotes = [];
    blockedChannels = [];
    GM_setValue("blockedEmotes", JSON.stringify(blockedEmotes, null, 2));
    GM_setValue("blockedChannels", JSON.stringify(blockedChannels, null, 2));
    console.log("[7BTTVFZ Control Emotes Panel] Очищены blockedEmotes и blockedChannels");
    updateBlockedList();
    updateCounter();
};


//----------------- export Button --------------------//
const exportButton = document.createElement('button');
exportButton.innerText = 'Export';
exportButton.style.background = buttonColor;
exportButton.style.color = '#fff';
exportButton.style.border = 'none';
exportButton.style.borderRadius = '4px';
exportButton.style.padding = '5px 10px';
exportButton.style.cursor = 'pointer';
exportButton.style.boxShadow = buttonShadow; // Тень для кнопки "Export"
buttonContainer.appendChild(exportButton);
exportButton.onclick = () => {
    const combinedData = {
        blockedEmotes: blockedEmotes,
        blockedChannels: blockedChannels
    };
    const blob = new Blob([JSON.stringify(combinedData, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = 'blocked_data.json';
    link.click();
    URL.revokeObjectURL(url);
    console.log("[7BTTVFZ Control Emotes Panel] Экспортированы данные:", combinedData);
};


//================= importButton ========================//
// Перемещаем создание fileInput в глобальную область, чтобы избежать дублирования
let fileInput = null;

// Функция для создания кнопки "Import"
function createImportButton() {
    const button = document.createElement('button');
    button.innerText = 'Import';
    button.style.background = buttonColor;
    button.style.color = '#fff';
    button.style.border = 'none';
    button.style.borderRadius = '4px';
    button.style.padding = '5px 10px';
    button.style.cursor = 'pointer';
    button.style.boxShadow = buttonShadow;
    return button;
}

// Функция для создания или переиспользования элемента input типа "file"
function createFileInput() {
    if (!fileInput) {
        fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.accept = 'application/json';
        fileInput.style.display = 'none';
        fileInput.onchange = handleFileChange;
        document.body.appendChild(fileInput);
    }
    return fileInput;
}

// Инициализация кнопки "Import"
const importButton = createImportButton();
buttonContainer.appendChild(importButton);

importButton.onclick = () => {
    const input = createFileInput();
    input.value = ''; // Сбрасываем значение для повторного выбора файла
    input.click();
};

// Обработка изменений файла
function handleFileChange(event) {
    const file = event.target.files[0];
    if (!file) {
        console.log("[7BTTVFZ Control Emotes Panel] Файл не выбран");
        return;
    }
    const reader = new FileReader();
    reader.onload = handleFileLoad;
    reader.onerror = () => {
        console.error("[7BTTVFZ Control Emotes Panel] Ошибка чтения файла");
        alert('Ошибка при чтении файла!');
    };
    reader.readAsText(file);
}

// Обработка загрузки файла
function handleFileLoad(event) {
    try {
        const importedData = JSON.parse(event.target.result);

        if (!importedData || (!importedData.blockedEmotes && !importedData.blockedChannels)) {
            throw new Error('Неверный формат файла! Ожидается объект с blockedEmotes и/или blockedChannels');
        }

        processImportedData(importedData);
        updateInterface();
        console.log("[7BTTVFZ Control Emotes Panel] Импорт успешно завершен");
    } catch (err) {
        console.error('[7BTTVFZ Control Emotes Panel] Ошибка при парсинге файла:', err);
        alert(`Ошибка импорта: ${err.message}`);
    }
}

// Обработка импортированных данных
function processImportedData(importedData) {
    blockedEmotes = [];
    blockedChannels = [];
    blockedEmoteIDs.clear();
    blockedChannelIDs.clear();
    newlyAddedIds.clear();

    if (Array.isArray(importedData.blockedEmotes)) {
        importedData.blockedEmotes.forEach(emote => {
            const newId = emote.id && !blockedEmoteIDs.has(emote.id) && !blockedChannelIDs.has(emote.id)
                ? emote.id
                : generateRandomID();

            const newEmote = {
                id: newId,
                name: emote.name || emote.emoteUrl || '',
                platform: emote.platform || 'unknown',
                emoteName: emote.emoteName || getDefaultEmoteName(emote),
                emoteUrl: emote.emoteUrl || emote.name || '',
                date: emote.date || new Date().toISOString()
            };

            blockedEmotes.push(newEmote);
            blockedEmoteIDs.add(newId);
            newlyAddedIds.add(newId);
        });
    }

    if (Array.isArray(importedData.blockedChannels)) {
        importedData.blockedChannels.forEach(channel => {
            const newId = channel.id && !blockedChannelIDs.has(channel.id) && !blockedEmoteIDs.has(channel.id)
                ? channel.id
                : generateRandomID();

            const newChannel = {
                id: newId,
                name: channel.name || channel.emoteUrl || '',
                platform: channel.platform || 'TwitchChannel',
                emoteName: channel.emoteName || getDefaultEmoteName(channel),
                emoteUrl: channel.emoteUrl || channel.name || '',
                date: channel.date || new Date().toISOString()
            };

            blockedChannels.push(newChannel);
            blockedChannelIDs.add(newId);
            newlyAddedIds.add(newId);
        });
    }

    GM_setValue("blockedEmotes", JSON.stringify(blockedEmotes, null, 2));
    GM_setValue("blockedChannels", JSON.stringify(blockedChannels, null, 2));
    console.log("[7BTTVFZ Control Emotes Panel] Импортированы blockedEmotes:", blockedEmotes);
    console.log("[7BTTVFZ Control Emotes Panel] Импортированы blockedChannels:", blockedChannels);
}

// Функция обновления интерфейса
function updateInterface() {
    blockedEmotes = loadData("blockedEmotes", []);
    blockedChannels = loadData("blockedChannels", []);

    blockedEmoteIDs.clear();
    blockedChannelIDs.clear();
    blockedEmotes.forEach(emote => blockedEmoteIDs.add(emote.id));
    blockedChannels.forEach(channel => blockedChannelIDs.add(channel.id));

    updateBlockedList();
    updateCounter();

    const chatContainer = document.querySelector('.chat-scrollable-area__message-container');
    if (chatContainer) {
        toggleEmotesInNode(chatContainer); // Используем toggleEmotesInNode вместо hideEmotesForChannel
    } else {
        console.log(
            "%c[7BTTVFZ Control Emotes Panel]%c Контейнер чата не найден при обновлении интерфейса",
            'color:rgb(218, 93, 9); font-weight: bold;',
            'color: rgb(218, 93, 9);'
        );
    }
}

// Функция скрытия эмодзи в чате
function hideEmotesForChannel(chatContainer) {
    console.log("[7BTTVFZ Control Emotes Panel] Запуск hideEmotesForChannel");
    const emotes = chatContainer.querySelectorAll('.chat-line__message img, .chat-line__message .emote, .chat-line__message .bttv-emote, .chat-line__message .seventv-emote');

    emotes.forEach(emote => {
        const emoteUrl = emote.src || '';
        const emoteAlt = emote.getAttribute('alt') || '';
        let blockedEntry = null;

        // Проверяем, заблокирован ли эмодзи
        if (emoteUrl.includes('7tv.app')) {
            blockedEntry = blockedEmotes.find(e => e.platform === '7tv' && e.emoteUrl === emoteUrl);
        } else if (emoteUrl.includes('betterttv.net')) {
            blockedEntry = blockedEmotes.find(e => e.platform === 'bttTV' && e.emoteUrl === emoteUrl);
        } else if (emoteAlt) {
            blockedEntry = blockedChannels.find(e => e.platform === 'TwitchChannel' && emoteAlt.startsWith(e.name));
        }

        // Устанавливаем data-emote-id, если эмодзи заблокирован
        if (blockedEntry && !emote.getAttribute('data-emote-id')) {
            emote.setAttribute('data-emote-id', blockedEntry.id);
        }

        const emoteId = emote.getAttribute('data-emote-id');
        const isBlocked = emoteId && (blockedChannels.some(e => e.id === emoteId) || blockedEmotes.some(e => e.id === emoteId));

        // Скрываем или показываем эмодзи
        emote.style.display = isBlocked ? 'none' : '';
        console.log(`[7BTTVFZ Control Emotes Panel] Эмодзи ${emoteAlt || emoteUrl} (ID: ${emoteId || 'не установлен'}) ${isBlocked ? 'скрыт' : 'показан'}`);
    });
}

// Функция получения имени эмотикона по умолчанию
function getDefaultEmoteName(channel) {
    if (channel.platform === '7tv' || channel.platform === 'bttTV') {
        return channel.name.split('/').slice(-2, -1)[0] || 'No Name';
    } else if (channel.platform === 'ffz') {
        return channel.emoteName || channel.name.split('/').pop() || 'No Name';
    } else if (channel.platform === 'TwitchChannel') {
        return channel.name.split(/[^a-zA-Z0-9]/)[0] || 'No Name';
    } else {
        return 'No Name';
    }
}





// Добавляем кнопку "Unblock All Emotes" в контейнер кнопок
const unblockAllButton = document.createElement('button');
unblockAllButton.innerText = 'Unblock All Emotes';
unblockAllButton.style.background = buttonColor;
unblockAllButton.style.color = '#fff';
unblockAllButton.style.border = 'none';
unblockAllButton.style.borderRadius = '4px';
unblockAllButton.style.padding = '5px 10px';
unblockAllButton.style.cursor = 'pointer';
unblockAllButton.style.boxShadow = buttonShadow; // Тень для кнопки "Unblock All Emotes"
buttonContainer.appendChild(unblockAllButton);

// Добавляем кнопку "Back To Block All Emotes" в контейнер кнопок
const blockAllButton = document.createElement('button');
blockAllButton.innerText = 'Back To Block All Emotes';
blockAllButton.style.background = buttonColor;
blockAllButton.style.color = '#fff';
blockAllButton.style.border = 'none';
blockAllButton.style.borderRadius = '4px';
blockAllButton.style.padding = '5px 10px';
blockAllButton.style.cursor = 'pointer';
blockAllButton.style.boxShadow = buttonShadow; // Тень для кнопки "Back To Block All Emotes"
buttonContainer.appendChild(blockAllButton);

// Обработчик события для кнопки "Unblock All Emotes"
unblockAllButton.onclick = () => {
    const unblockedEmotes = GM_getValue('unblockedEmotes', []);
    const unblockedChannels = GM_getValue('unblockedChannels', []);
    if (blockedEmotes.length > 0 || blockedChannels.length > 0) {
        GM_setValue('unblockedEmotes', blockedEmotes);
        GM_setValue('unblockedChannels', blockedChannels);
        blockedEmotes = [];
        blockedChannels = [];
        GM_setValue('blockedEmotes', JSON.stringify(blockedEmotes, null, 2)); // Исправлено
        GM_setValue('blockedChannels', JSON.stringify(blockedChannels, null, 2)); // Исправлено
        console.log("[7BTTVFZ Control Emotes Panel] Разблокированы все: unblockedEmotes:", blockedEmotes, "unblockedChannels:", blockedChannels);
        updateBlockedList();
        updateCounter();
        showAllEmotes();
    }
};

// Функция для отображения всех смайлов в чате
function showAllEmotes() {
    const chatContainer = document.querySelector('.chat-scrollable-area__message-container');
    if (chatContainer) {
        const emotes = chatContainer.querySelectorAll('.chat-line__message img, .chat-line__message .emote, .chat-line__message .bttv-emote, .chat-line__message .seventv-emote');
        emotes.forEach(emote => {
            emote.style.display = ''; // Сбросить стиль display для отображения смайлов
        });
    }
}

// Обработчик события для кнопки "Back To Block All Emotes"
blockAllButton.onclick = () => {
    const unblockedEmotes = GM_getValue('unblockedEmotes', []);
    const unblockedChannels = GM_getValue('unblockedChannels', []);
    if (unblockedEmotes.length > 0 || unblockedChannels.length > 0) {
        blockedEmotes = unblockedEmotes;
        blockedChannels = unblockedChannels;
        GM_setValue('blockedEmotes', JSON.stringify(blockedEmotes));
        GM_setValue('blockedChannels', JSON.stringify(blockedChannels));
        GM_setValue('unblockedEmotes', []);
        GM_setValue('unblockedChannels', []);
        console.log("[7BTTVFZ Control Emotes Panel] Заблокированы все обратно: blockedEmotes:", blockedEmotes, "blockedChannels:", blockedChannels);

        // Обновляем список и счетчик
        updateBlockedList();
        updateCounter();

        // Применяем скрытие эмодзи в чате
        const chatContainer = document.querySelector('.chat-scrollable-area__message-container');
        if (chatContainer) {
            toggleEmotesInNode(chatContainer);
            console.log("[7BTTVFZ Control Emotes Panel] Применено скрытие эмодзи после восстановления блокировки");
        } else {
            console.log(
                "%c[7BTTVFZ Control Emotes Panel]%c Контейнер чата не найден при восстановлении блокировки",
                'color:rgb(218, 93, 9); font-weight: bold;',
                'color: rgb(218, 93, 9);'
            );
        }
    }
};



// Кнопка "Show Stats Chart"
const showStatsButton = document.createElement('button');
showStatsButton.innerText = 'Show Stats Chart';
showStatsButton.style.cursor = 'pointer';
showStatsButton.style.position = 'relative';
showStatsButton.style.right = '1%';



// Создаём модальное окно для диаграммы
const chartModal = document.createElement('div');
chartModal.style.position = 'fixed';
chartModal.style.top = '35%';
chartModal.style.left = '35%';
chartModal.style.width = '35%';
chartModal.style.height = '55%';
chartModal.style.display = 'none'; // Скрыто по умолчанию
chartModal.style.justifyContent = 'center';
chartModal.style.alignItems = 'center';
chartModal.style.zIndex = '10001';

// Создаём контейнер для диаграммы
const chartContainer = document.createElement('div');
chartContainer.style.background = 'linear-gradient(315deg, hsla(285, 61%, 12%, 1) 0%, hsla(186, 26%, 21%, 1) 55%, hsla(284, 9%, 48%, 1) 100%)';chartContainer.style.padding = '20px';
chartContainer.style.borderRadius = '20px';
chartContainer.style.boxShadow = '16px 20px 20px 5px #0000008c';
chartContainer.style.border = '2px solid #24888e';
chartContainer.style.position = 'relative';
chartContainer.style.width = '600px';
chartContainer.style.maxHeight = '80vh';
chartContainer.style.overflowY = 'auto';

// Создаём кнопку закрытия модального окна
const closeChartButton = document.createElement('button');
closeChartButton.innerText = 'Close';
closeChartButton.style.position = 'absolute';
closeChartButton.style.top = '10px';
closeChartButton.style.right = '10px';
closeChartButton.style.background = '#944646';
closeChartButton.style.color = '#fff';
closeChartButton.style.border = 'none';
closeChartButton.style.borderRadius = '4px';
closeChartButton.style.padding = '5px 10px';
closeChartButton.style.cursor = 'pointer';
closeChartButton.onclick = () => {
    chartModal.style.display = 'none';
    // Уничтожаем диаграмму, чтобы избежать утечек памяти
    const existingChart = Chart.getChart('statsChart');
    if (existingChart) {
        existingChart.destroy();
    }
};

// Создаём элемент canvas для диаграммы
const chartCanvas = document.createElement('canvas');
chartCanvas.id = 'statsChart';
chartCanvas.style.maxWidth = '100%';
chartCanvas.style.height = '400px';

// Добавляем элементы в модальное окно
chartContainer.appendChild(closeChartButton);
chartContainer.appendChild(chartCanvas);
chartModal.appendChild(chartContainer);
document.body.appendChild(chartModal);

// Обработчик клика на кнопку "Show Stats Chart"
showStatsButton.onclick = () => {
    // Показываем модальное окно
    chartModal.style.display = 'flex';

    // Собираем данные для диаграммы
    const twitchCount = blockedChannels.length;
    const bttvCount = blockedEmotes.filter(channel => channel.platform === 'bttTV').length;
    const tv7Count = blockedEmotes.filter(channel => channel.platform === '7tv').length;
    const ffzCount = blockedEmotes.filter(channel => channel.platform === 'ffz').length;

    // Данные для диаграммы
    const chartData = {
        labels: ['Twitch', 'BTTV', '7TV', 'FFZ'],
        datasets: [{
            label: 'Blocked Emotes by Platform',
            data: [twitchCount, bttvCount, tv7Count, ffzCount],
            backgroundColor: [
                'rgba(96, 37, 136, 0.6)',  // Twitch
                'rgba(214, 95, 91, 0.6)',  // BTTV
                'rgba(34, 196, 196, 0.6)',  // 7TV
                'rgba(121, 117, 117, 0.66)'   // FFZ
            ],
            borderColor: [
                'rgb(130, 255, 99)',
                'rgb(97, 183, 240)',
                'rgb(255, 238, 86)',
                'rgb(74, 221, 221)'
            ],
            borderWidth: 1
        }]
    };

    // Уничтожаем старую диаграмму, если она существует
    const existingChart = Chart.getChart('statsChart');
    if (existingChart) {
        existingChart.destroy();
    }

    // Создаём новую диаграмму
    new Chart(chartCanvas, {
        type: 'bar',
        data: chartData,
        options: {
            responsive: true,
            maintainAspectRatio: false,
            plugins: {
                legend: {
                    position: 'top',
                    labels: {
                        color: '#E5E7EB' // Цвет текста легенды
                    }
                },
                title: {
                    display: true,
                    text: 'Blocked Emotes by Platform',
                    color: ' #E5E7EB' // Цвет текста заголовка
                }
            },
            scales: {
                y: {
                    beginAtZero: true,
                    title: {
                        display: true,
                        text: 'Number of Blocked Emotes',
                        color: ' #E5E7EB' // Цвет текста заголовка оси Y
                    },
                    ticks: {
                        color: ' #E5E7EB' // Цвет текста значений на оси Y
                    }
                },
                x: {
                    title: {
                        display: true,
                        text: 'Platform',
                        color: ' #E5E7EB' // Цвет текста заголовка оси X
                    },
                    ticks: {
                        color: ' #E5E7EB' // Цвет текста значений на оси X
                    }
                }
            }
        }
    });
};


//======================= Счётчик ========================//
const counter = document.createElement('div');
counter.style.display = 'flex';
counter.style.flexDirection = 'row';
counter.style.justifyContent = 'center';
counter.style.width = '460px';
counter.style.backgroundColor = ' #b69dcf'; // Белый фон
counter.style.color = ' #4c2a5e'; // Цвет текста (темно-фиолетовый)
counter.style.border = '3px solid #4c2a5e'; // Граница того же цвета, что и текст
counter.style.borderRadius = '8px'; // Радиус скругления границы
counter.style.padding = '5px 0px'; // Отступы для удобства
counter.style.marginLeft = '6px'; // Отступ слева для отделения от других элементов
counter.style.fontWeight = 'bold'; // Жирное начертание текста
counter.style.fontSize = '16px'; // Устанавливаем размер шрифта для лучшей видимости
counter.style.bottom = '545px'; // Обновленное положение сверху
counter.style.left = '202px'; // Обновленное положение справа
counter.style.position = 'relative '; // Относительное позиционирование для точного расположения

controlPanel.appendChild(counter);

// Функция для обновления счётчика
function updateCounter() {
    const twitchCount = blockedChannels.length;
    const bttvCount = blockedEmotes.filter(channel => channel.platform === 'bttTV').length;
    const tv7Count = blockedEmotes.filter(channel => channel.platform === '7tv').length;
    const ffzCount = blockedEmotes.filter(channel => channel.platform === 'ffz').length;
    const totalCount = twitchCount + bttvCount + tv7Count + ffzCount;
    counter.innerText = `Twitch: ${twitchCount} | BTTV: ${bttvCount} | 7TV: ${tv7Count} | FFZ: ${ffzCount} | Total: ${totalCount}`;
}

// Добавляем элементы на страницу
inputContainer.appendChild(input);
inputContainer.appendChild(addButton);
controlPanel.appendChild(inputContainer);

// Перемещаем контейнер кнопок вниз
controlPanel.appendChild(buttonContainer);

document.body.appendChild(controlPanel);

// Вызываем функцию обновления счётчика
updateCounter();





// Загружаем сохранённое состояние переключателя из хранилища

//============= Создаем кнопку "Open Blocker Emote" ===================//
const openPanelButton = document.createElement('button');
openPanelButton.innerText = 'panel control emotes';
openPanelButton.style.fontWeight = 'bold';
openPanelButton.style.top = '22px';
openPanelButton.style.right = '1344px';
openPanelButton.style.position = 'fixed'; // Фиксированное положение
openPanelButton.style.width = '200px'; // Фиксированная ширина кнопки
openPanelButton.style.height = '41px'; // Фиксированная высота кнопки
openPanelButton.style.background = ' #5d5d5d'; // Цвет кнопки
openPanelButton.style.color = ' #171c1c';
openPanelButton.style.border = 'none'; // Без границ
openPanelButton.style.borderRadius = '20px'; // Закругленные углы
openPanelButton.style.padding = '10px';
openPanelButton.style.cursor = 'pointer';
openPanelButton.style.zIndex = 10000; // Высокий z-index
openPanelButton.style.transition = 'background 0.3s ease'; // Плавное изменение фона
openPanelButton.style.display = 'flex';
openPanelButton.style.alignItems = 'center';
openPanelButton.style.justifyContent = 'space-between'; // Чтобы текст и переключатель были по разным краям

// Создаем контейнер для переключателя (темная рамка)
const switchContainer = document.createElement('div');
switchContainer.style.width = '44px'; // Увеличиваем ширину контейнера на 6px
switchContainer.style.height = '27px'; // Увеличиваем высоту контейнера на 6px
switchContainer.style.borderRadius = '13px'; // Скругленные углы
switchContainer.style.backgroundColor = ' #171c1c';  // Темно сеая рамка для кружка
switchContainer.style.position = 'relative'; // Для абсолютного позиционирования кружка
switchContainer.style.transition = 'background 0.3s ease'; // Плавное изменение фона контейнера
openPanelButton.appendChild(switchContainer);

// Создаем фиолетовый кружок (переключатель кружок )
const switchCircle = document.createElement('div');
switchCircle.style.width = '19px'; // Увеличиваем ширину кружка на 3px
switchCircle.style.height = '19px'; // Увеличиваем высоту кружка на 3px
switchCircle.style.borderRadius = '50%'; // Кружок
switchCircle.style.backgroundColor = ' #5d5d5d'; // темно Серый  цвет кружка
switchCircle.style.boxShadow = '0 2px 6px rgba(0, 0, 0, 0.8)'; // Тень для кружка
switchCircle.style.position = 'absolute'; // Абсолютное позиционирование внутри контейнера
switchCircle.style.top = '3px'; // Отступ сверху
switchCircle.style.left = '3px'; // Отступ слева
switchCircle.style.transition = 'transform 0.3s ease'; // Плавное движение
switchContainer.appendChild(switchCircle);

// Функция для обновления состояния переключателя
const updateSwitchState = () => {
    if (isPanelOpen) {
        openPanelButton.style.background = ' #5d5d5d'; // Цвет кнопки при открытой панели
        switchCircle.style.transform = 'translateX(20px)'; // Перемещаем кружок вправо
        switchContainer.style.backgroundColor = ' #464646'; // Цвет контейнера в включённом состоянии
        controlPanel.style.display = 'block'; // Показываем панель
        controlPanel.style.height = '656px'; // Устанавливаем полную высоту
    } else {
        openPanelButton.style.background = ' #5d5d5d'; // Цвет кнопки при закрытой панели
        switchCircle.style.transform = 'translateX(0)'; // Перемещаем кружок влево
        switchContainer.style.backgroundColor = ' #171c1c'; // Цвет контейнера в выключенном состоянии
        controlPanel.style.display = 'none'; // Скрываем панель
        controlPanel.style.height = '0px'; // Сворачиваем панель
    }
};

// Обработчик клика для переключения состояния панели
openPanelButton.onclick = () => {
    isPanelOpen = !isPanelOpen; // Переключаем состояние
    GM_setValue('isPanelOpen', isPanelOpen); // Сохраняем состояние
    updateSwitchState(); // Обновляем видимость и переключатель
};


// Инициализация состояния при загрузке
window.addEventListener('load', () => {
    document.body.appendChild(openPanelButton);
    updateSwitchState(); // Устанавливаем начальное состояние панели и переключателя

    const updateButtonPosition = () => {
        const windowWidth = window.innerWidth;
        const windowHeight = window.innerHeight;
        openPanelButton.style.top = `${windowHeight * 0.005}px`; // 5% от высоты окна
        openPanelButton.style.right = `${windowWidth * 0.2}px`; // 20% от ширины окна
    };

    updateButtonPosition();
    window.addEventListener('resize', updateButtonPosition);
});







//=============== Блокировка и Запуск скрытия эмодзи в чате ==================//

//=============== Генерация случайного ID ===============//
function generateRandomID() {
    const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    const randomLength = Math.floor(Math.random() * 67) + 1; // Случайная длина от 1 до 68
    let randomID = '';
    for (let i = 0; i < randomLength; i++) {
        randomID += characters.charAt(Math.floor(Math.random() * characters.length));
    }
    return `emote_${randomID}`;
}



// Оптимизированная версия toggleEmotesInNode
const debouncedToggleEmotes = debounce(toggleEmotesInNode, 100);

async function toggleEmotesInNode(node) {
    try {
        console.log(`%c[${new Date().toISOString()}] %c[7BTTVFZ Control Emotes Panel] %ctoggleEmotesInNode - starting`,
            'color: rgb(63, 136, 219);',
            'color: rgb(52, 163, 148); font-weight: bold;');

        const emotes = node.querySelectorAll('.chat-line__message img, .chat-line__message .emote, .chat-line__message .bttv-emote, .chat-line__message .seventv-emote');
        console.log(`[7BTTVFZ Control Emotes Panel] Найдено эмодзи для обработки: ${emotes.length}`);

        for (const emote of emotes) {
            const emoteUrl = emote.src || emote.getAttribute('srcset')?.split(' ')[0] || '';
            const emoteAlt = emote.getAttribute('alt') || '';
            let blockedEntry = null;

            if (emoteUrl.includes('7tv.app')) {
                blockedEntry = blockedEmotes.find(e => e.platform === '7tv' && e.emoteUrl === emoteUrl);
            } else if (emoteUrl.includes('betterttv.net')) {
                blockedEntry = blockedEmotes.find(e => e.platform === 'bttTV' && e.emoteUrl === emoteUrl);
            } else if (emoteUrl.includes('frankerfacez.com')) {
                blockedEntry = blockedEmotes.find(e => e.platform === 'ffz' && e.emoteUrl === emoteUrl);
            } else if (emoteAlt) {
                blockedEntry = blockedChannels.find(e => e.platform === 'TwitchChannel' && emoteAlt.startsWith(e.name));
            }

            if (blockedEntry && !emote.getAttribute('data-emote-id')) {
                emote.setAttribute('data-emote-id', blockedEntry.id);
            }

            const emoteId = emote.getAttribute('data-emote-id');
            const isBlocked = emoteId && (blockedChannels.some(e => e.id === emoteId) || blockedEmotes.some(e => e.id === emoteId));

            emote.style.display = isBlocked ? 'none' : '';
            console.log(`[7BTTVFZ Control Emotes Panel] Эмодзи ${emoteAlt || emoteUrl} (ID: ${emoteId || 'не установлен'}) ${isBlocked ? 'скрыт' : 'показан'}`);
        }

        console.log(`%c[${new Date().toISOString()}] %c[7BTTVFZ Control Emotes Panel] %ctoggleEmotesInNode - completed`,
            'color: rgb(63, 136, 219);',
            'color: rgb(52, 163, 148); font-weight: bold;');
    } catch (error) {
        console.error(`[ERROR] Ошибка в toggleEmotesInNode:`, error);
    }
}
// Используем дебаунс в наблюдателе
const observer = new MutationObserver(mutations => {
    mutations.forEach(mutation => {
        mutation.addedNodes.forEach(node => {
            if (node.nodeType === 1) {
                console.log(`%cНовый узел добавлен в DOM`,
                    'color:rgb(29, 202, 136) ;');
                debouncedToggleEmotes(node);
            }
        });
    });
});



function observeChatContainer() {
    const chatContainer = document.querySelector('.chat-scrollable-area__message-container');
    if (chatContainer) {
        // Успешно - зеленый цвет
           console.log(
             '%c[7BTTVFZ Control Emotes Panel]%c Контейнер чата найден, начинаем наблюдение',
             'color: #00C4B4; font-weight: bold;', // Стиль для [7BTTVFZ Control Emotes Panel]
             'color: #00C4B4;' // Стиль для остального текста
           );
        observer.disconnect(); // Останавливаем старое наблюдение
        observer.observe(chatContainer, { childList: true, subtree: true });
        toggleEmotesInNode(chatContainer); // Проверяем существующие сообщения
    } else {
        // Неуспешно  - красный цвет
            console.log(
              '%c[7BTTVFZ Control Emotes Panel]%c Контейнер чата не найден, повторная попытка через 500мс',
              'color: #FF5555; font-weight: bold;', // Стиль для [7BTTVFZ Control Emotes Panel]
              'color: #FF5555;' // Стиль для остального текста
            );
        setTimeout(observeChatContainer, 500);
    }
}

// Добавляем наблюдение за изменениями на более высоком уровне DOM
function startRootObserver() {
    const rootObserver = new MutationObserver(() => {
        const chatContainer = document.querySelector('.chat-scrollable-area__message-container');
        // Состояние контейнера чата - зеленый если найден, красный если не найден
           console.log(
             '%c[7BTTVFZ Control Emotes Panel]%c RootObserver: контейнер чата %c' + (chatContainer ? 'найден' : 'не найден'),
             'color: #1E90FF; font-weight: bold;', // Стиль для [7BTTVFZ Control Emotes Panel] (DodgerBlue)
             'color: #1E90FF;', // Стиль для "RootObserver: контейнер чата"
             `color: ${chatContainer ? '#00C4B4' : '#FF5555'}; font-weight: bold;` // Зеленый (#00C4B4) или красный (#FF5555) для статуса
           );

        if (chatContainer) {
            observeChatContainer();
        }
    });
    rootObserver.observe(document.body, { childList: true, subtree: true });
    // Запуск RootObserver - синий цвет (информационный)
         console.log(
           '%c[7BTTVFZ Control Emotes Panel]%c RootObserver запущен',
           'color: #1E90FF; font-weight: bold;', // Стиль для [7BTTVFZ Control Emotes Panel] (DodgerBlue)
           'color: #1E90FF;' // Стиль для остального текста
         );

}

// Запускаем наблюдение
startRootObserver();


let lastUrl = location.href;

function checkUrlChange() {
    const currentUrl = location.href;
    if (currentUrl !== lastUrl) {
        console.log('[7BTTVFZ Control Emotes Panel] URL изменился, перезапускаем наблюдение за чатом');
        ContextMenuManager.removeMenu(); // Удаляем контекстное меню
        lastUrl = currentUrl;
        observeChatContainer();
    }
    setTimeout(checkUrlChange, 1000);
}

checkUrlChange();




//=============== Контекстное меню ===============//
const contextMenuStyle = document.createElement('style');
contextMenuStyle.innerHTML = `
  .custom-context-menu {
     position: absolute;
     background:rgb(19, 88, 39);
     border: 1px solid #ccc;
     padding: 5px;
     z-index: 10002;
     cursor: pointer;
     color: #fff;
     transition: background 0.3s ease;
     user-select: none;
     min-width: 150px;
     box-shadow: 0 2px 8px 2px #8BC34A;
     border-radius: 8px;
   }
     .custom-context-menu:hover {
        background:rgb(16, 68, 30);
   }
`;
document.head.appendChild(contextMenuStyle);

const ContextMenuManager = {
    menu: null,
    isProcessing: false, // Флаг для предотвращения многократных нажатий

    createMenu(event, emotePrefix, platform, emoteName) {
        this.removeMenu();
        const menu = document.createElement('div');
        menu.className = 'custom-context-menu';
        menu.style.top = `${event.pageY}px`;
        menu.style.left = `${event.pageX}px`;
        menu.innerText = `Block Emote (${emoteName || 'Unknown'})`;
        console.log(`%c[${new Date().toISOString()}] %c[7BTTVFZ Control Emotes Panel] %cContext menu created at:`,
            'color: rgb(85, 113, 165);',
            'color: rgb(85, 113, 165); font-weight: bold;',
            'color: rgb(85, 113, 165);', event.pageX, event.pageY);

        document.body.appendChild(menu);
        this.menu = menu;

        menu.addEventListener('click', (e) => {
            e.stopPropagation();
            if (this.isProcessing) return; // Пропускаем, если обработка уже идет
            this.isProcessing = true;

            console.log(`%c[${new Date().toISOString()}] %c[7BTTVFZ Control Emotes Panel] %cBlocking emote: ${emoteName}`,
                'color: rgb(209, 89, 129);',
                'color: rgb(255, 50, 50); font-weight: bold;',
                'color: rgb(209, 89, 129);');

            this.blockEmote(emotePrefix, platform, emoteName);
            this.removeMenu();
            this.isProcessing = false;
        });

        document.addEventListener('click', (e) => {
            if (!menu.contains(e.target)) this.removeMenu();
        }, { once: true });
    },

    removeMenu() {
        if (this.menu) {
            console.log(`%c[${new Date().toISOString()}] %c[7BTTVFZ Control Emotes Panel] %cRemoving context menu`,
                'color: rgb(209, 89, 129);',
                'color: rgb(115, 2, 160); font-weight: bold;',
                'color: white;');
            this.menu.remove();
            this.menu = null;
        }
    },

    blockEmote(emotePrefix, platform, emoteName) {
        const emoteId = generateRandomID();
        const currentDateTime = new Date().toISOString();
        const newEntry = {
            id: emoteId,
            name: emotePrefix, // Префикс (например, "guwu")
            platform: platform,
            emoteName: emoteName || emotePrefix.split('/').pop() || 'Unknown', // Полное название (например, "guwuPopcorn")
            emoteUrl: platform === 'TwitchChannel' ? emotePrefix : emotePrefix, // Для Twitch используем префикс как URL
            date: currentDateTime
        };

        const isDuplicate = platform === 'TwitchChannel'
            ? blockedChannels.some(e => e.name === newEntry.name && e.platform === newEntry.platform)
            : blockedEmotes.some(e => e.emoteUrl === newEntry.emoteUrl && e.platform === newEntry.platform);

        if (isDuplicate) {
            console.log(`%c[7BTTVFZ Control Emotes Panel] %cEmote already blocked: ${newEntry.emoteName}`,
                'color: rgb(255, 165, 0); font-weight: bold;',
                'color: rgb(255, 165, 0);');
            return;
        }

        if (platform === 'TwitchChannel') {
            blockedChannels.push(newEntry);
            blockedChannelIDs.add(emoteId);
            newlyAddedIds.add(emoteId);
            GM_setValue("blockedChannels", JSON.stringify(blockedChannels, null, 2));
            console.log(`%c[7BTTVFZ Control Emotes Panel] %cAdded to blockedChannels:`,
                'color: rgb(0, 255, 0); font-weight: bold;',
                'color: rgb(0, 255, 0);', newEntry);
        } else {
            blockedEmotes.push(newEntry);
            blockedEmoteIDs.add(emoteId);
            newlyAddedIds.add(emoteId);
            GM_setValue("blockedEmotes", JSON.stringify(blockedEmotes, null, 2));
            console.log(`%c[7BTTVFZ Control Emotes Panel] %cAdded to blockedEmotes:`,
                'color: rgb(0, 255, 0); font-weight: bold;',
                'color: rgb(0, 255, 0);', newEntry);
        }

        const chatContainer = document.querySelector('.chat-scrollable-area__message-container');
        if (chatContainer) {
            toggleEmotesInNode(chatContainer);
        }

        updateBlockedList();
        updateCounter();
    }
};

//=============== Обработчик контекстного меню ===============//
// блокировка сайлов правой кнопкой //
document.addEventListener('contextmenu', (event) => {
    const target = event.target;
    if (target.tagName === 'IMG' && target.closest('.chat-line__message')) {
        event.preventDefault();
        const emoteUrl = target.src || target.getAttribute('srcset')?.split(' ')[0] || '';
        const emoteAlt = target.getAttribute('alt') || '';
        const dataProvider = target.getAttribute('data-provider') || '';
        let emotePrefix = '';
        let platform = '';
        let emoteName = emoteAlt;

        console.log(`[${new Date().toISOString()}] [7BTTVFZ Control Emotes Panel] Context menu triggered for:`, emoteUrl, emoteAlt, 'data-provider:', dataProvider);

        // Определяем платформу и префикс
        if (dataProvider === 'bttv' && emoteUrl.includes('betterttv.net')) {
            emotePrefix = emoteUrl || `https://cdn.betterttv.net/emote/${target.getAttribute('data-id')}/2x.webp`;
            platform = 'bttTV';
            console.log("[7BTTVFZ Control Emotes Panel] Detected bttv emote (via data-provider):", emotePrefix);
        } else if (dataProvider === 'ffz' && emoteUrl.includes('frankerfacez.com')) {
            emotePrefix = emoteUrl || `https://cdn.frankerfacez.com/emote/${target.getAttribute('data-id')}/2`;
            platform = 'ffz';
            emoteName = emoteAlt;
            console.log("[7BTTVFZ Control Emotes Panel] Detected ffz emote (via data-provider):", emotePrefix);
        } else if (dataProvider === 'ffz' && emoteUrl.includes('7tv.app')) {
            emotePrefix = emoteUrl || `https://cdn.7tv.app/emote/${target.getAttribute('data-id')}/2x.webp`;
            platform = '7tv';
            console.log("[7BTTVFZ Control Emotes Panel] Detected 7tv emote (via data-provider):", emotePrefix);
        } else if (emoteUrl.includes('betterttv.net')) {
            emotePrefix = emoteUrl;
            platform = 'bttTV';
            console.log("[7BTTVFZ Control Emotes Panel] Detected bttv emote (via URL):", emoteUrl);
        } else if (emoteUrl.includes('7tv.app')) {
            emotePrefix = emoteUrl;
            platform = '7tv';
            console.log("[7BTTVFZ Control Emotes Panel] Detected 7tv emote (via URL):", emoteUrl);
        } else if (emoteUrl.includes('frankerfacez.com')) {
            emotePrefix = emoteUrl;
            platform = 'ffz';
            emoteName = emoteAlt;
            console.log("[7BTTVFZ Control Emotes Panel] Detected ffz emote (via URL):", emoteUrl);
        } else if (emoteAlt) {
            // Обновленная логика для TwitchChannel
            const match = emoteAlt.match(/^([a-z0-9]+)([A-Z].*)$/); // Ищем префикс до первой заглавной буквы
            if (match) {
                emotePrefix = match[1]; // Например, "lowti3" из "lowti3Face3"
                emoteName = emoteAlt;   // Полное название, например "lowti3Face3"
            } else {
                // Если не удалось разделить, используем первую группу символов до не-букв/цифр как запасной вариант
                emotePrefix = emoteAlt.split(/[^a-zA-Z0-9]/)[0] || emoteAlt;
                emoteName = emoteAlt;
            }
            platform = 'TwitchChannel';
            console.log("[7BTTVFZ Control Emotes Panel] Detected TwitchChannel emote:", emoteAlt, "prefix:", emotePrefix);
        }

        if (emotePrefix && platform) {
            console.log(`[7BTTVFZ Control Emotes Panel] Creating context menu for emote with prefix: ${emotePrefix}, platform: ${platform}`);
            ContextMenuManager.createMenu(event, emotePrefix, platform, emoteName);
        } else {
            console.log("[7BTTVFZ Control Emotes Panel] Could not determine platform or prefix, using fallback TwitchChannel");
            ContextMenuManager.createMenu(event, emoteAlt || emoteUrl, 'TwitchChannel', emoteAlt || 'Unknown');
        }
    }
});

//=============== Запуск ===============//
observeChatContainer();







//====================== Управление высотой панели =======================
function closePanel() {
    isPanelOpen = false;
    GM_setValue('isPanelOpen', isPanelOpen);
    controlPanel.style.height = '0px'; // Плавно уменьшаем высоту
    setTimeout(() => {
        if (!isPanelOpen) controlPanel.style.display = 'none'; // Полностью скрываем после завершения анимации
    }, 150); // Таймер соответствует времени анимации
}

//----------------- Анимация сворачивания панели-------------------------
function openPanel() {
    isPanelOpen = true;
    GM_setValue('isPanelOpen', isPanelOpen);
    controlPanel.style.display = 'block'; // Делаем панель видимой
    setTimeout(() => {
        controlPanel.style.height = '656px'; // Плавно увеличиваем высоту
    }, 0); // Устанавливаем высоту с задержкой для работы анимации
}

//========================== Переключение состояния панели Управления 'openPanelButton' ===============================
openPanelButton.onclick = () => {
    isPanelOpen = !isPanelOpen; // Переключаем состояние панели (открыта/закрыта)
    GM_setValue('isPanelOpen', isPanelOpen);

    // Перемещаем переключатель (круглый элемент), когда панель открывается или закрывается
    switchCircle.style.transform = isPanelOpen ? 'translateX(20px)' : 'translateX(0)';

    // Меняем цвет фона контейнера в зависимости от состояния панели
     // switchContainer.style.backgroundColor = isPanelOpen ? ' #9e9e9e' : ' #171c1c'; //
     // закоментируем убрав  временно для будущих версий  switchContainer //

    // Переключаем видимость панели: открываем или закрываем
    if (isPanelOpen) {
        openPanel(); // Вызов функции для открытия панели
    } else {
        closePanel(); // Вызов функции для закрытия панели
    }
};

// Инициализация состояния
updateSwitchState(); // Убедимся, что переключатель синхронизирован с начальным состоянием
updateBlockedList();
updateCounter();





//============== Минипанель с кнопками сортировки по категориям =================//
const sortContainer = document.createElement('div');
sortContainer.id = 'sortContainer';
sortContainer.style.display = 'flex';
sortContainer.style.justifyContent = 'space-around';
sortContainer.style.backgroundColor = ' rgb(89 51 114)';
sortContainer.style.padding = '5px';
sortContainer.style.marginBottom = '37px';
sortContainer.style.position = 'relative';
sortContainer.style.top = '57px';
sortContainer.style.borderRadius = '8px 8px 0 0'; // Закругление только верхних углов
sortContainer.style.border = '1px solid rgb(255, 255, 255)';
sortContainer.style.boxShadow = ' rgb(0 0 0 / 0%) 0px 15px 6px 0px'; // Использование RGBA для прозрачности
sortContainer.style.zIndex = 'inherit'; // Наследует z-index от родителя

// Определение начальных значений для currentSortOrder
let currentSortOrder = {
    name: 'asc',
    platform: 'asc',
    date: 'asc'
};

// Кнопки сортировки
const sortByNameButton = document.createElement('button');
sortByNameButton.innerHTML = 'Name ▲';
sortByNameButton.style.cursor = 'pointer';
sortByNameButton.style.position = 'relative';
sortByNameButton.style.left = '13%';

sortByNameButton.onclick = () => {
    const order = currentSortOrder.name === 'asc' ? 'desc' : 'asc';
    currentSortOrder.name = order;
    sortByNameButton.innerHTML = `Name ${order === 'asc' ? '▲' : '▼'}`; // Переключение стрелочки
    sortblockedEmotes('name', order);
};
sortContainer.appendChild(sortByNameButton);

const sortByPlatformButton = document.createElement('button');
sortByPlatformButton.innerHTML = 'Platform ▲';
sortByPlatformButton.style.cursor = 'pointer';
sortByPlatformButton.style.position = 'relative';
sortByPlatformButton.style.right = '118px';

sortByPlatformButton.onclick = () => {
    const order = currentSortOrder.platform === 'asc' ? 'desc' : 'asc';
    currentSortOrder.platform = order;
    sortByPlatformButton.innerHTML = `Platform ${order === 'asc' ? '▲' : '▼'}`;
    sortblockedEmotes('platform', order);
};
sortContainer.appendChild(sortByPlatformButton);

const sortByDateButton = document.createElement('button');
sortByDateButton.innerHTML = 'Date-Time ▲';
sortByDateButton.style.cursor = 'pointer';
sortByDateButton.style.top = '0px';
sortByDateButton.style.position = 'relative';
sortByDateButton.style.left = '9px';
sortByDateButton.onclick = () => {
    const order = currentSortOrder.date === 'asc' ? 'desc' : 'asc';
    currentSortOrder.date = order;
    sortByDateButton.innerHTML = `Date ${order === 'asc' ? '▲' : '▼'}`;
    sortblockedEmotes('date', order);
};
sortContainer.appendChild(sortByDateButton);

// Добавляем контейнер сортировки в панель управления
controlPanel.insertBefore(sortContainer, title);


// ---------- goToLast    Button ------------- //
const goToLastButton = document.createElement('button');
goToLastButton.innerHTML = 'Go To Last Element ▼'; // Короткое название
goToLastButton.style.cursor = 'pointer';
goToLastButton.style.position = 'relative';
goToLastButton.style.right = '2%'; // Сдвигаем чуть левее для баланса

goToLastButton.onclick = () => {
    goToLastAddedItem();
};
sortContainer.appendChild(goToLastButton);
sortContainer.appendChild(showStatsButton);




//============== Функция для сортировки списка =================//
function sortblockedEmotes(criteria, order) {
    const sortFunc = (a, b) => {
        let comparison = 0;
        if (criteria === 'name') {
            comparison = a.emoteName.localeCompare(b.emoteName);
        } else if (criteria === 'platform') {
            comparison = a.platform.localeCompare(b.platform);
        } else if (criteria === 'date') {
            comparison = new Date(a.date) - new Date(b.date);
        }
        return order === 'asc' ? comparison : -comparison;
    };

    // Сортируем оба массива
    blockedEmotes.sort(sortFunc);
    blockedChannels.sort(sortFunc);

    // Обновляем интерфейс после сортировки
    updateBlockedList();
}
//============== Обработчики событий для кнопок =================//
const buttons = [addButton, clearAllButton, exportButton, importButton, unblockAllButton, blockAllButton];
buttons.forEach(button => {
    button.onmouseover = function() {
        button.style.background = '-webkit-linear-gradient(135deg, #443157 0%,rgb(90, 69, 122) 56%, #443157 98%, #443157 100%)'; // Изменение фона при наведении
    };
    button.onmouseout = function() {
        button.style.background = buttonColor; // Возвращаем исходный цвет
    };
});



// ========= Функция для прокрутки к последнему добавленному элементу ============= //
function goToLastAddedItem() {
    const allItems = [...blockedEmotes, ...blockedChannels];
    if (allItems.length === 0) {
        console.log("[7BTTVFZ Control Emotes Panel] Список пуст, некуда прокручивать");
        return;
    }

    // Находим элемент с самой поздней датой
    const lastItem = allItems.reduce((latest, current) => {
        return new Date(current.date) > new Date(latest.date) ? current : latest;
    });

    // Ищем элемент в DOM по его ID
    let lastElement = list.querySelector(`[data-id="${lastItem.id}"]`);
    if (lastElement) {
        // Подсвечиваем элемент
        lastElement.classList.add('last-item-highlight');

        // Прокручиваем к элементу
        const itemOffsetTop = lastElement.offsetTop;
        const listHeight = list.clientHeight;
        const itemHeight = lastElement.clientHeight;
        const scrollPosition = itemOffsetTop - (listHeight / 2) + (itemHeight / 2);
        list.scrollTo({
            top: scrollPosition,
            behavior: 'smooth'
        });

        // Убираем подсветку через 60 секунд
        setTimeout(() => {
            lastElement.classList.remove('last-item-highlight');
            console.log(`[7BTTVFZ Control Emotes Panel] Подсветка убрана с элемента: ${lastItem.emoteName}`);
        }, 60000); // 60000 мс = 1 минута

        console.log(`[7BTTVFZ Control Emotes Panel] Прокручено и подсвечено: ${lastItem.emoteName} (ID: ${lastItem.id})`);
    } else {
        console.log("[7BTTVFZ Control Emotes Panel] Последний элемент не найден в DOM, обновляем список");
        updateBlockedList();
        setTimeout(() => {
            lastElement = list.querySelector(`[data-id="${lastItem.id}"]`);
            if (lastElement) {
                lastElement.classList.add('last-item-highlight');
                const itemOffsetTop = lastElement.offsetTop;
                const listHeight = list.clientHeight;
                const itemHeight = lastElement.clientHeight;
                const scrollPosition = itemOffsetTop - (listHeight / 2) + (itemHeight / 2);
                list.scrollTo({
                    top: scrollPosition,
                    behavior: 'smooth'
                });
                setTimeout(() => {
                    lastElement.classList.remove('last-item-highlight');
                    console.log(`[7BTTVFZ Control Emotes Panel] Подсветка убрана с элемента после обновления: ${lastItem.emoteName}`);
                }, 60000);
                console.log(`[7BTTVFZ Control Emotes Panel] Успешно прокручено и подсвечено после обновления: ${lastItem.emoteName}`);
            }
        }, 100);
    }
}

console.log(getComputedStyle(controlPanel).display);
console.log("[7BTTVFZ Control Emotes Panel] Opening control panel...");
console.log("[7BTTVFZ Control Emotes Panel] Creating control panel...");
console.log("[7BTTVFZ Control Emotes Panel] Adding button...");
console.log("[7BTTVFZ Control Emotes Panel] Updating channel list...");
console.log("[7BTTVFZ Control Emotes Panel] Creating file input element...");
// Удаляем некорректные логи с event, так как они не в контексте события
console.log("[7BTTVFZ Control Emotes Panel] Processing imported channels...");
console.log("[7BTTVFZ Control Emotes Panel] Updating interface...");
console.log("[7BTTVFZ Control Emotes Panel] Showing all emotes in chat...");
console.log("[7BTTVFZ Control Emotes Panel] Blocking all emotes...");
console.log("[7BTTVFZ Control Emotes Panel] Hiding emotes for a channel...");
console.log(`%c[7BTTVFZ Control Emotes Panel] %cWaiting for chat container...`,
    'color:rgb(255, 114, 173); font-weight: bold;', // Стиль для [7BTTVFZ Control Emotes Panel]
    'color: rgb(255, 114, 173)  ;'); // Стиль для остального текста

console.log("[7BTTVFZ Control Emotes Panel] Creating context menu...");





// Добавляем переменные для отслеживания состояния
let lastKnownBlockedCount = blockedEmotes.length + blockedChannels.length;
let lastCheckTime = Date.now();
let isRestarting = false;

// Функция проверки состояния блокировки
function checkBlockingStatus() {
    console.log(`%c[WATCHDOG] %cПроверка состояния блокировки...`,
        'color:rgb(221, 101, 175); font-weight: bold;',
        'color: rgb(164, 207, 44) ;');

    const chatContainer = document.querySelector('.chat-scrollable-area__message-container');
    if (!chatContainer) {
        console.log(
            "%c[WATCHDOG]%c Контейнер чата не найден, перезапускаем наблюдение",
            'color:rgb(172, 147, 223); font-weight: bold;',
            'color: rgb(164, 207, 44) ;');
        observeChatContainer(); // Перезапускаем наблюдение
        return false;
    }

    const emotes = chatContainer.querySelectorAll('.chat-line__message img, .chat-line__message .emote, .chat-line__message .bttv-emote, .chat-line__message .seventv-emote');
    if (emotes.length === 0) {
        console.log("[WATCHDOG] Эмодзи в чате не найдены, пропускаем проверку");
        return true;
    }

    let failureDetected = false;

    emotes.forEach((emote, index) => {
        if (index > 5) return;
        const emoteId = emote.getAttribute('data-emote-id');
        const shouldBeBlocked = emoteId && (blockedChannels.some(e => e.id === emoteId) || blockedEmotes.some(e => e.id === emoteId));
        const isVisible = emote.style.display !== 'none';

        if (shouldBeBlocked && isVisible) {
            console.log(`[WATCHDOG] Обнаружен сбой: эмодзи с ID ${emoteId} должен быть скрыт, но виден!`);
            failureDetected = true;
        } else if (!shouldBeBlocked && !isVisible) {
            console.log(`[WATCHDOG] Обнаружен сбой: эмодзи с ID ${emoteId} не должен быть скрыт, но скрыт!`);
            failureDetected = true;
        }
    });

    const currentBlockedCount = blockedEmotes.length + blockedChannels.length;
    if (currentBlockedCount !== lastKnownBlockedCount) {
        console.log(
            `%c[WATCHDOG] %cКоличество заблокированных элементов изменилось: %c${lastKnownBlockedCount} -> ${currentBlockedCount}`,
            'color: rgb(221, 101, 175); font-weight: bold;',
            'color: rgb(164, 207, 44);',
            'color: rgb(255, 165, 0); font-weight: bold;'
        );
        lastKnownBlockedCount = currentBlockedCount;
    }

    return !failureDetected;
}

function showNotification(message, duration = 3000) {
    const notification = document.createElement('div');
    notification.innerText = message; // Добавляем текст
    notification.style.position = 'relative';
    notification.style.bottom = '99%';
    notification.style.maxWidth = '155px';
    notification.style.left = '61%';
    notification.style.backgroundColor = '#341d41';
    notification.style.color = ' #30aa54';
    notification.style.padding = '6px';
    notification.style.borderRadius = '40px';
    notification.style.boxShadow = 'rgb(130, 113, 148) 1px 1px 7px 4px';
    notification.style.zIndex = '1001';
    notification.style.fontSize = '10px';

    // Начальные стили для анимации (уменьшенный размер)
    notification.style.transform = 'scale(0)'; // Начинаем с масштаба 0
    notification.style.opacity = '0'; // Начинаем с прозрачности 0
    notification.style.transition = 'transform 0.3s ease, opacity 0.3s ease'; // Плавный переход для масштаба и прозрачности

    document.body.appendChild(notification);

    // Запускаем анимацию увеличения после добавления в DOM
    setTimeout(() => {
        notification.style.transform = 'scale(1)'; // Увеличиваем до нормального размера
        notification.style.opacity = '1'; // Делаем полностью видимым
    }, 10); // Небольшая задержка для запуска перехода

    // Удаляем уведомление после завершения длительности
    setTimeout(() => {
        // Добавляем анимацию исчезновения перед удалением (опционально)
        notification.style.transform = 'scale(0)';
        notification.style.opacity = '0';
        setTimeout(() => {
            notification.remove();
        }, 300); // Соответствует времени transition
    }, duration);
}

// Функция перезапуска логики блокировки
function restartBlockingLogic() {
    if (isRestarting) return;
    isRestarting = true;
   // Перезапуск логики - оранжевый цвет (в процессе)
          console.log(
            '%c[WATCHDOG]%c Перезапуск логики блокировки...',
            'color: #FF4500; font-weight: bold;', // Стиль для [WATCHDOG] (OrangeRed)
            'color: #FF4500;' // Стиль для остального текста
          );
    showNotification(" chat not found ... waiting... ", 3000); // уведомление о перезапуске когда сбой  failure

    observer.disconnect();
    const chatContainer = document.querySelector('.chat-scrollable-area__message-container');
    if (chatContainer) {
        const emotes = chatContainer.querySelectorAll('.chat-line__message img, .chat-line__message .emote, .chat-line__message .bttv-emote, .chat-line__message .seventv-emote');
        emotes.forEach(emote => {
            emote.style.display = '';
            emote.removeAttribute('data-emote-id');
        });
        observeChatContainer();
        toggleEmotesInNode(chatContainer);
    } else {
        observeChatContainer();
    }

    updateBlockedList();
    updateCounter();
    setTimeout(() => {
        isRestarting = false;
        // Перезапуск завершен - зеленый цвет (успех)
              console.log(
                '%c[WATCHDOG]%c Перезапуск завершен',
                'color: #00C4B4; font-weight: bold;', // Стиль для [WATCHDOG] (Teal)
                'color: #00C4B4;' // Стиль для остального текста
              );
    }, 1000); // Задержка для предотвращения спама
}

// Периодическая проверка состояния (watchdog)
function startWatchdog() {
    const interval = GM_getValue('watchdogInterval', 10) * 1000; // Секунды в миллисекунды
    setInterval(() => {
        const currentTime = Date.now();
        if (currentTime - lastCheckTime < 5000) return;
        lastCheckTime = currentTime;

        const isWorking = checkBlockingStatus();
        if (!isWorking) {
            console.log(
                '%c[WATCHDOG]%c Обнаружен сбой в работе блокировки, перезапуск...',
                'color: #FFA500; font-weight: bold;',
                'color: #FFA500;'
            );
            restartBlockingLogic();
        } else {
            console.log(
                `%c[WATCHDOG] %cБлокировка работает корректно!`,
                'color:rgb(6, 167, 0); font-weight: bold;',
                'color: rgb(164, 207, 44);'
            );
        }
    }, interval); // Используем динамический интервал
}


    //================  Модуль управления темами ================== //
    (function () {
        // Определяем начальный массив тем
       //================ Модуль управления темами ================== //
(function () {
    // Резервные темы (используются, если загрузка с GitHub не удалась)
    const fallbackThemes = [
        {
            name: 'default',
            displayName: 'Default Theme',
            styles: {
                controlPanel: {
                    background: '-webkit-linear-gradient(270deg, hsla(50, 76%, 56%, 1) 0%, hsla(32, 83%, 49%, 1) 25%, hsla(0, 37%, 37%, 1) 59%, hsla(276, 47%, 24%, 1) 79%, hsla(261, 11%, 53%, 1) 100%)',
                    border: '1px solid #ccc',
                    boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
                    color: '#fff'
                },
                lastItemHighlight: {
                    backgroundColor: '#ffd700'
                },
                openPanelButton: {
                    background: '#4c2a5e',
                    color: '#bda3d7',
                    border: 'none'
                },
                switchContainer: {
                    backgroundColor: '#ccb8eb5c',
                    activeBackgroundColor: '#bda3d7'
                },
                switchCircle: {
                    backgroundColor: '#4c2a5e',
                    boxShadow: '0 2px 6px rgba(0, 0, 0, 0.8)'
                },
                list: {
                    background: '-webkit-linear-gradient(45deg, hsla(292, 44%, 16%, 1) 0%, hsla(173, 29%, 48%, 1) 100%)',
                    border: '1px solid #ffffff',
                    color: '#fff',
                    scrollbarThumb: '#C1A5EF',
                    scrollbarTrack: '#455565'
                },
                counter: {
                    backgroundColor: '#b69dcf',
                    color: '#4c2a5e',
                    border: '3px solid #4c2a5e'
                },
                sortContainer: {
                    backgroundColor: 'rgb(89, 51, 114)',
                    border: '1px solid rgb(255, 255, 255)',
                    color: '#fff'
                },
                title: {
                    color: '#2a1e38'
                },
                buttons: {
                    background: '#907cad',
                    color: '#fff'
                },
                versionLabel: {
                    color: 'rgb(62, 33, 85)'
                },
                searchInput: {
                    background: '#192427',
                    color: '#b69dcf',
                    border: '1px solid #b69dcf'
                },
                input: {
                    background: '#192427',
                    color: '#b69dcf',
                    border: '1px solid #b69dcf'
                },
                themeSelect: {
                    background: '#192427',
                    color: '#b69dcf',
                    border: '1px solid #c1a5ef'
                },
                platformSelect: {
                    background: '#192427',
                    color: '#b69dcf',
                    border: '1px solid #c1a5ef'
                },
                deleteButton: {
                    background: '#944646',
                    color: '#fff',
                    hoverBackground: 'linear-gradient(135deg, #480a0c 56%, #ca5d5f 98%, #8b4040 100%)'
                },
                listItemText: {
                    color: '#ffffff'
                },
                listItemLink: {
                    color: '#b3e0f2'
                },
                listItemDate: {
                    color: '#cccccc'
                },
                chartWrapper: {
                    backgroundColor: '#fff',
                    border: '1px solid #ffffff',
                    color: '#000'
                }
            }
        }
    ];

    // Загружаем кэшированные темы или используем резервные
    let themes = GM_getValue('themes', fallbackThemes);
    let selectedThemeName = GM_getValue('selectedTheme', 'default');

    // Функция для загрузки тем с GitHub
    async function loadThemesFromGitHub() {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: 'https://raw.githubusercontent.com/sopernik566/Control_Emotes_Panel_Twitch_JS/refs/heads/main/themes.json',
                onload: function (response) {
                    try {
                        const loadedThemes = JSON.parse(response.responseText);
                        console.log('[7BTV Control Emotes Panel] Темы загружены с GitHub:', loadedThemes);
                        themes = loadedThemes;
                        GM_setValue('themes', themes); // Кэшируем темы локально
                        resolve(themes);
                    } catch (err) {
                        console.error('[7BTV Control Emotes Panel] Ошибка парсинга тем с GitHub:', err);
                        reject(err);
                    }
                },
                onerror: function (err) {
                    console.error('[7BTV Control Emotes Panel] Ошибка загрузки тем с GitHub:', err);
                    reject(err);
                }
            });
        });
    }

    // Функция для сохранения тем в хранилище
    function saveThemes() {
        GM_setValue('themes', themes);
        console.log('[7BTV Control Emotes Panel] Темы сохранены в хранилище:', themes);
    }

    // Функция для сохранения выбранной темы
    function saveSelectedTheme(themeName) {
        selectedThemeName = themeName;
        GM_setValue('selectedTheme', themeName);
        console.log('[7BTV Control Emotes Panel] Выбранная тема сохранена:', themeName);
    }

    // Функция для применения темы
    function applyTheme(themeName) {
        const theme = themes.find(t => t.name === themeName) || themes[0];
        if (!theme) {
            console.warn(`[7BTV Control Emotes Panel] Тема ${themeName} не найдена, используется 'default'`);
            applyTheme('default');
            return;
        }

        console.log(`[7BTV Control Emotes Panel] Применение темы: ${themeName}`);

        const currentPanelDisplay = controlPanel.style.display;
        const currentPanelHeight = controlPanel.style.height;

        if (openPanelButton) {
            Object.assign(openPanelButton.style, theme.styles.openPanelButton);
            openPanelButton.style.position = 'fixed';
            openPanelButton.style.zIndex = '10000';
            openPanelButton.style.transition = 'background 0.3s ease';
        }

        if (switchContainer) {
            Object.assign(switchContainer.style, {
                backgroundColor: isPanelOpen ? theme.styles.switchContainer.activeBackgroundColor : theme.styles.switchContainer.backgroundColor,
                width: '44px',
                height: '27px',
                borderRadius: '13px',
                position: 'relative',
                transition: 'background 0.3s ease'
            });
        }

        if (switchCircle) {
            Object.assign(switchCircle.style, theme.styles.switchCircle);
            switchCircle.style.width = '19px';
            switchCircle.style.height = '19px';
            switchCircle.style.borderRadius = '50%';
            switchCircle.style.position = 'absolute';
            switchCircle.style.top = '3px';
            switchCircle.style.left = '3px';
            switchCircle.style.transition = 'transform 0.3s ease';
        }

        if (controlPanel) {
            Object.assign(controlPanel.style, theme.styles.controlPanel);
            controlPanel.style.display = currentPanelDisplay;
            controlPanel.style.height = currentPanelHeight;
            controlPanel.style.transition = 'height 0.3s ease';
        }

        if (list) {
            Object.assign(list.style, theme.styles.list);
            const styleElement = document.getElementById('customScrollbarStyle') || document.createElement('style');
            styleElement.id = 'customScrollbarStyle';
            styleElement.innerHTML = `
                #blockedList::-webkit-scrollbar { width: 25px; }
                #blockedList::-webkit-scrollbar-thumb {
                    background-color: ${theme.styles.list.scrollbarThumb};
                    border-radius: 8px;
                    border: 3px solid #4F3E6A;
                    height: 80px;
                }
                #blockedList::-webkit-scrollbar-thumb:hover { background-color: ${theme.styles.list.scrollbarThumb}; }
                #blockedList::-webkit-scrollbar-thumb:active { background-color: ${theme.styles.list.scrollbarThumb}; }
                #blockedList::-webkit-scrollbar-track {
                    background: ${theme.styles.list.scrollbarTrack};
                    border-radius: 0px 0px 8px 0px;
                }
                #blockedList::-webkit-scrollbar-track:hover { background: ${theme.styles.list.scrollbarTrack}; }
                #blockedList::-webkit-scrollbar-track:active { background: ${theme.styles.list.scrollbarTrack}; }
            `;
            if (!document.getElementById('customScrollbarStyle')) {
                document.head.appendChild(styleElement);
            }
        }

        const lastItemHighlightStyle = document.createElement('style');
        lastItemHighlightStyle.id = 'lastItemHighlightStyle';
        lastItemHighlightStyle.innerHTML = `
            .last-item-highlight {
                background-color: ${theme.styles.lastItemHighlight?.backgroundColor || '#ffd700'};
                transition: background-color 0.5s ease;
            }
        `;
        const existingStyle = document.getElementById('lastItemHighlightStyle');
        if (existingStyle) {
            existingStyle.remove();
        }
        document.head.appendChild(lastItemHighlightStyle);

        if (counter) {
            Object.assign(counter.style, theme.styles.counter);
            counter.style.display = 'flex';
        }

        if (sortContainer) {
            Object.assign(sortContainer.style, theme.styles.sortContainer);
            sortContainer.style.display = 'flex';
        }

        if (title) {
            Object.assign(title.style, theme.styles.title);
            title.style.display = 'block';
        }

        const buttons = [addButton, clearAllButton, exportButton, importButton, unblockAllButton, blockAllButton, searchButton];
        buttons.forEach(button => {
            Object.assign(button.style, theme.styles.buttons);
            button.onmouseover = () => {
                button.style.background = '-webkit-linear-gradient(135deg, #443157 0%, rgb(90, 69, 122) 56%, #443157 98%, #443157 100%)';
            };
            button.onmouseout = () => {
                button.style.background = theme.styles.buttons.background;
            };
        });

        if (versionLabel) {
            Object.assign(versionLabel.style, theme.styles.versionLabel);
        }

        if (searchInput) {
            Object.assign(searchInput.style, theme.styles.searchInput);
        }

        if (input) {
            Object.assign(input.style, theme.styles.input);
        }

        if (platformSelect) {
            Object.assign(platformSelect.style, theme.styles.platformSelect);
        }

        if (themeSelect) {
            Object.assign(themeSelect.style, theme.styles.themeSelect);
        }

        const deleteButtons = list.querySelectorAll('.delete-button');
        deleteButtons.forEach(button => {
            Object.assign(button.style, theme.styles.deleteButton, {
                height: '35px',
                width: '75px',
                fontWeight: 'bold',
                fontSize: '16px',
                border: 'none',
                borderRadius: '4px',
                cursor: 'pointer',
                boxShadow: '0 4px 8px rgba(0, 0, 0, 0.6)',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                transition: 'background 0.3s ease'
            });
            button.onmouseover = () => {
                button.style.background = theme.styles.deleteButton.hoverBackground;
            };
            button.onmouseout = () => {
                button.style.background = theme.styles.deleteButton.background;
            };
        });

        currentDeleteButtonStyles = {
            background: theme.styles.deleteButton.background,
            color: theme.styles.deleteButton.color,
            hoverBackground: theme.styles.deleteButton.hoverBackground
        };
        console.log('[7BTV Control Emotes Panel] Сохранены стили кнопки Delete:', currentDeleteButtonStyles);

        if (list) {
            const listItemTexts = list.querySelectorAll('.list-item-text');
            const listItemLinks = list.querySelectorAll('.list-item-link');
            const listItemDates = list.querySelectorAll('.list-item-date');
            listItemTexts.forEach(span => {
                Object.assign(span.style, theme.styles.listItemText);
            });
            listItemLinks.forEach(span => {
                Object.assign(span.style, theme.styles.listItemLink);
            });
            listItemDates.forEach(span => {
                Object.assign(span.style, theme.styles.listItemDate);
            });
        }

        saveSelectedTheme(themeName);
    }

    // Функция для обновления селектора тем
    function updateThemeSelector() {
        themeSelect.innerHTML = ''; // Очищаем текущие опции
        themes.forEach(theme => {
            const option = document.createElement('option');
            option.value = theme.name;
            option.innerText = theme.displayName;
            if (theme.name === selectedThemeName) {
                option.selected = true;
            }
            themeSelect.appendChild(option);
        });
    }

    // Инициализация интерфейса выбора тем
    const themeSelectorContainer = document.createElement('div');
    themeSelectorContainer.style.position = 'relative';
    themeSelectorContainer.style.bottom = '100px';
    themeSelectorContainer.style.width = '126px';
    themeSelectorContainer.style.left = '0px';
    themeSelectorContainer.style.zIndex = '10001';

    const themeSelect = document.createElement('select');
    themeSelect.style.padding = '5px';
    themeSelect.style.height = '35px';
    themeSelect.style.width = '126px';
    themeSelect.style.borderRadius = '4px';
    themeSelect.style.background = '#192427';
    themeSelect.style.color = '#b69dcf';
    themeSelect.style.border = '1px solid #b69dcf';

    themeSelect.onchange = () => {
        const selectedTheme = themeSelect.value;
        applyTheme(selectedTheme);
    };

    themeSelectorContainer.appendChild(themeSelect);
    controlPanel.appendChild(themeSelectorContainer);

    // Загружаем темы с GitHub и инициализируем интерфейс
    loadThemesFromGitHub()
        .then(() => {
            updateThemeSelector();
            applyTheme(selectedThemeName);
        })
        .catch(() => {
            console.warn('[7BTV Control Emotes Panel] Не удалось загрузить темы с GitHub, использую кэшированные или резервные');
            updateThemeSelector();
            applyTheme(selectedThemeName);
        }); 


// Запускаем watchdog
startWatchdog();
})();

})()})();