Greasy Fork

Greasy Fork is available in English.

Panel Control XFilter 2.4.5 (c) tapeavion

Hide posts by keywords with the dashboard and hide posts from verified accounts

当前为 2025-03-23 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Panel Control  XFilter 2.4.5 (c) tapeavion
// @namespace    http://tampermonkey.net/
// @version      2.4.5
// @description  Hide posts by keywords with the dashboard and hide posts from verified accounts
// @author       gullampis810
// @match        https://x.com/*
// @match      https://x.com/i/grok*
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// @icon         https://www.pinclipart.com/picdir/big/450-4507608_twitter-circle-clipart.png
// ==/UserScript==

(function () {
    "use strict";

    // ===== Настройки и инициализация ===== //
    const STORAGE_KEY = "hiddenKeywords";
    let hiddenKeywords = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];
    let unblockedKeywords = JSON.parse(localStorage.getItem("unblockedKeywords")) || [];
    let hideVerifiedAccounts = false;
    let hideNonVerifiedAccounts = JSON.parse(localStorage.getItem("hideNonVerifiedAccounts")) || false;
    const languageFilters = {
        english: /[a-zA-Z]/,
        russian: /[А-Яа-яЁё]/,
        japanese: /[\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Han}]/u,
        ukrainian: /[А-Яа-яІіЄєЇїҐґ]/,
        belarusian: /[А-Яа-яЎўЁёІі]/,
        tatar: /[А-Яа-яӘәӨөҮүҖҗ]/,
        mongolian: /[\p{Script=Mongolian}]/u,
        chinese: /[\p{Script=Han}]/u,
        german: /[a-zA-ZßÄäÖöÜü]/,
        polish: /[a-zA-ZąćęłńóśźżĄĆĘŁŃÓŚŹŻ]/,
        french: /[a-zA-Zàâçéèêëîïôûùüÿ]/,
        swedish: /[a-zA-ZåäöÅÄÖ]/,
        estonian: /[a-zA-ZäõöüÄÕÖÜ]/,
        danish: /[a-zA-Z帿ŨÆ]/,
        turkish: /[a-zA-ZıİçÇğĞöÖşŞüÜ]/,
        portuguese: /[a-zA-Zàáâãçéêíóôõúü]/,
    };
    let activeLanguageFilters = {};

    // ===== Сохранение в localStorage ===== //
    function saveKeywords() {
        localStorage.setItem(STORAGE_KEY, JSON.stringify(hiddenKeywords));
    }

    function saveUnblockedKeywords() {
        localStorage.setItem("unblockedKeywords", JSON.stringify(unblockedKeywords));
    }

    // ===== Функция для обновления отображения по языкам ===== //
    function updateLanguageFilter(language) {
        if (activeLanguageFilters[language]) {
            delete activeLanguageFilters[language];
        } else {
            activeLanguageFilters[language] = languageFilters[language];
        }
        hidePosts();
    }

    // ===== Проверка языка текста ===== //
    function isTextInLanguage(text) {
        for (const [language, regex] of Object.entries(activeLanguageFilters)) {
            if (regex.test(text)) {
                return true;
            }
        }
        return false;
    }

    // ===== Функция Скрытия постов ===== //
    function hidePosts() {
        document.querySelectorAll("article").forEach((article) => {
            const textContent = article.innerText.toLowerCase();
            const isVerifiedAccount = article.querySelector('[data-testid="icon-verified"]');

            const matchesKeyword = hiddenKeywords.some((keyword) => {
                try {
                    return new RegExp(keyword, "i").test(textContent);
                } catch (e) {
                    return textContent.includes(keyword.toLowerCase());
                }
            });

            const shouldHideVerified = hideVerifiedAccounts && isVerifiedAccount;
            const shouldHideNonVerified = hideNonVerifiedAccounts && !isVerifiedAccount;
            const shouldHideByLanguage = isTextInLanguage(textContent);
            const shouldHideByKeyword = matchesKeyword;

            if (shouldHideVerified || shouldHideNonVerified || shouldHideByLanguage || shouldHideByKeyword) {
                article.style.display = "none";
            } else {
                article.style.display = "";
            }
        });
    }

    // ===== Создание панели управления ===== //
    const panel = document.createElement("div");
    panel.style.position = "fixed";
    panel.style.bottom = "62px";
    panel.style.right = "180px";
    panel.style.width = "335px";
    panel.style.height = "310px";
    panel.style.padding = "8px";
    panel.style.fontFamily = "Arial, sans-serif";
    panel.style.backgroundColor = "#34506c";
    panel.style.color = "#fff";
    panel.style.borderRadius = "8px";
    panel.style.boxShadow = "0 0 10px rgba(0,0,0,0.5)";
    panel.style.zIndex = "9999";
    panel.style.overflow = "auto";
    panel.style.transition = "height 0.3s ease";

    panel.innerHTML = `
        <h3 style="margin: 0; font-size: 16px;">Hiding Control</h3>
        <input id="keywordInput" type="text" placeholder="Enter the word" style="width: calc(100% - 95px); height: 30px; padding: 5px; margin: 10px 0; border-radius: 5px; border: none;">
        <div style="display: flex; flex-wrap: wrap; gap: 5px; position: relative;">
            <button id="addKeyword" style="flex: 1; min-width: 70px; max-width: 70px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px;">
                Add it
            </button>
            <button id="exportKeywords" style="flex: 1; min-width: 60px; max-width: 70px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px;">
                Export
            </button>
            <button id="importKeywords" style="flex: 1; min-width: 60px; max-width: 70px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px;">
                Import
            </button>
            <button id="toggleBlockKeywords" style="flex: 1; min-width: 80px; max-width: 90px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; font-size: 13px; font-weight: bold;">
                Unblock All
            </button>
            <button id="clearKeywords" style="flex: 1; min-width: 60px; max-width: 80px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; bottom: 0px; position: relative; left: 1px;">
                Clear all
            </button>
            <button id="toggleVerifiedPosts" style="width: 242px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; font-size: 13px; font-weight: bold;">
                Hide verified accounts: Click to Disable
            </button>
            <button id="toggleNonVerifiedPosts" style="width: 242px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; font-size: 13px; font-weight: bold;">
                Hide non-verified accounts: Turn ON
            </button>
            <button id="openLanguagePopup" style="width: 80px; display: flex; align-content: center; flex-wrap: wrap; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; font-size: 13px; font-weight: bold;">
                Language Filtering
            </button>
        </div>
        <ul id="keywordList" style="list-style: position: relative; inside; padding: 0; margin-top: 10px; font-size: 14px; color: #fff;"></ul>
    `;

    document.body.appendChild(panel);

    const lengthFilterInput = document.createElement("input");
    lengthFilterInput.type = "number";
    lengthFilterInput.placeholder = "Min length";
    lengthFilterInput.style.width = "80px";
    lengthFilterInput.style.marginTop = "10px";
    panel.appendChild(lengthFilterInput);

    lengthFilterInput.addEventListener("change", () => {
        const minLength = parseInt(lengthFilterInput.value) || 0;
        debouncedHidePosts();
    });

    // ===== Перетаскивание панели ===== //
    let isDragging = false;
    let offsetX = 0;
    let offsetY = 0;

    panel.addEventListener("mousedown", (event) => {
        isDragging = true;
        offsetX = event.clientX - panel.offsetLeft;
        offsetY = event.clientY - panel.offsetTop;
        panel.style.cursor = "grabbing";
    });

    document.addEventListener("mousemove", (event) => {
        if (isDragging) {
            panel.style.left = event.clientX - offsetX + "px";
            panel.style.top = event.clientY - offsetY + "px";
        }
    });

    document.addEventListener("mouseup", () => {
        isDragging = false;
        panel.style.cursor = "grab";
    });

    // Создаем попап для выбора языков
    const languagePopup = document.createElement("div");
    languagePopup.style.display = "none";
    languagePopup.style.position = "fixed";
    languagePopup.style.top = "460px";
    languagePopup.style.right = "65px";
    languagePopup.style.transform = "translate(-52%, 7%)";
    languagePopup.style.backgroundColor = "rgb(52, 80, 108)";
    languagePopup.style.padding = "20px";
    languagePopup.style.borderRadius = "8px";
    languagePopup.style.zIndex = "10000";
    languagePopup.style.width = "8%";
    languagePopup.style.boxShadow = "rgba(0, 0, 0, 0.5) 0px 0px 10px";
    languagePopup.style.fontFamily = "Arial, sans-serif";

    for (const language in languageFilters) {
        const checkbox = document.createElement("input");
        checkbox.type = "checkbox";
        checkbox.id = `lang-${language}`;
        checkbox.name = language;

        const label = document.createElement("label");
        label.htmlFor = `lang-${language}`;
        label.textContent = language.charAt(0).toUpperCase() + language.slice(1);

        const wrapper = document.createElement("div");
        wrapper.appendChild(checkbox);
        wrapper.appendChild(label);

        languagePopup.appendChild(wrapper);
    }

    const closeButton = document.createElement("button");
    closeButton.textContent = "X";
    closeButton.style.position = "relative";
    closeButton.style.width = "40px";
    closeButton.style.height = "40px";
    closeButton.style.borderRadius = "50%";
    closeButton.style.backgroundColor = "rgb(32, 49, 66)";
    closeButton.style.color = "rgb(255, 255, 255)";
    closeButton.style.border = "none";
    closeButton.style.display = "flex";
    closeButton.style.alignItems = "center";
    closeButton.style.justifyContent = "center";
    closeButton.style.cursor = "pointer";
    closeButton.style.marginTop = "10px";
    closeButton.style.boxShadow = "rgba(0, 0, 0, 0.1) 0px 4px 6px";
    closeButton.style.transition = "background-color 0.3s";
    closeButton.style.fontFamily = "Arial, sans-serif";
    closeButton.style.left = "82%";
    closeButton.style.top = "56px";
    closeButton.addEventListener("click", () => {
        languagePopup.style.display = "none";
    });
    languagePopup.appendChild(closeButton);

    document.body.appendChild(languagePopup);

    document.getElementById("openLanguagePopup").addEventListener("click", () => {
        languagePopup.style.display = "block";
    });

    const warningText = document.createElement("div");
    warningText.textContent = "⚠️it may stops working";
    warningText.style.color = "#ffcc00";
    warningText.style.fontSize = "14px";
    warningText.style.marginBottom = "10px";
    warningText.style.textAlign = "end";
    warningText.style.right = "38px";
    warningText.style.position = "relative";
    warningText.style.top = "20px";
    languagePopup.appendChild(warningText);

    for (const language in languageFilters) {
        document.getElementById(`lang-${language}`).addEventListener("change", () => {
            updateLanguageFilter(language);
        });
    }

    // Стили для подсветки
    const style = document.createElement("style");
    style.textContent = `
        button {
            transition: box-shadow 0.3s, transform 0.3s;
        }
        button:hover {
            box-shadow: 0 0 10px rgba(255, 255, 255, 0.7);
        }
        button:active {
            transform: scale(0.95);
            box-shadow: 0 0 5px rgba(255, 255, 255, 0.7);
        }
        ::-webkit-scrollbar {
            width: 25px;
        }
        ::-webkit-scrollbar-thumb {
            background-color: #C1A5EF;
            border-radius: 8px;
            border: 3px solid #4F3E6A;
            height: 80px;
        }
        ::-webkit-scrollbar-thumb:hover {
            background-color: #C6AEFF;
        }
        ::-webkit-scrollbar-thumb:active {
            background-color: #B097C9;
        }
        ::-webkit-scrollbar-track {
            background: rgba(69, 85, 101, 0);
            border-radius: 0px 0px 8px 0px;
        }
        ::-webkit-scrollbar-track:hover {
            background-color: rgba(69, 85, 101, 0);
        }
        ::-webkit-scrollbar-track:active {
            background-color: rgba(69, 85, 101, 0);
        }
    `;
    document.head.appendChild(style);

    // ======== Кнопка iOS-переключатель Panel FilterX ========= //
    let isSwitchOn = localStorage.getItem("isSwitchOff") === "true";
    const toggleButton = document.createElement("div");
    toggleButton.style.position = "fixed";
    toggleButton.style.top = "94%";
    toggleButton.style.right = "90px";
    toggleButton.style.width = "192px";
    toggleButton.style.display = "flex";
    toggleButton.style.alignItems = "center";
    toggleButton.style.gap = "10px";
    toggleButton.style.zIndex = "1";
    toggleButton.style.background = "#15202b";
    toggleButton.style.border = "4px solid #6c7e8e";
    toggleButton.style.borderRadius = "18px";
    toggleButton.style.boxSizing = "border-box";

    // Функция для проверки, является ли текущая страница Grok
    function isGrokPage() {
        return window.location.href.startsWith("https://x.com/i/grok");
    }

    // Изначально скрываем кнопку на странице Grok
    toggleButton.style.display = isGrokPage() ? "none" : "flex";

    const toggleLabel = document.createElement("label");
    toggleLabel.style.display = "inline-block";
    toggleLabel.style.width = "50px";
    toggleLabel.style.height = "25px";
    toggleLabel.style.borderRadius = "25px";
    toggleLabel.style.backgroundColor = "#0d1319";
    toggleLabel.style.position = "relative";
    toggleLabel.style.cursor = "pointer";
    toggleLabel.style.transition = "background-color 0.3s";

    const toggleSwitch = document.createElement("div");
    toggleSwitch.style.position = "absolute";
    toggleSwitch.style.width = "21px";
    toggleSwitch.style.height = "21px";
    toggleSwitch.style.borderRadius = "50%";
    toggleSwitch.style.backgroundColor = "#6c7e8e";
    toggleSwitch.style.top = "2px";
    toggleSwitch.style.left = isSwitchOn ? "calc(100% - 23px)" : "2px";
    toggleSwitch.style.transition = "left 0.3s ease";
    toggleSwitch.style.boxShadow = "rgb(21, 32, 43) -1px 1px 4px 1px";

    toggleLabel.style.backgroundColor = isSwitchOn ? "#425364" : "#0d1319";

    function toggleSwitchState() {
        isSwitchOn = !isSwitchOn;
        localStorage.setItem("isSwitchOn", isSwitchOn.toString());
        toggleSwitch.style.left = isSwitchOn ? "calc(100% - 23px)" : "2px";
        toggleLabel.style.backgroundColor = isSwitchOn ? "#425364" : "#0d1319";
    }

    toggleButton.addEventListener("click", toggleSwitchState);
    toggleLabel.appendChild(toggleSwitch);
    toggleButton.appendChild(toggleLabel);
    document.body.appendChild(toggleButton);

    const toggleText = document.createElement("span");
    toggleText.style.position = "relative";
    toggleText.style.right = "5px";
    toggleText.textContent = "Panel FilterX";
    toggleText.style.color = "#6c7e8e";
    toggleText.style.fontFamily = "Arial, sans-serif";
    toggleText.style.fontSize = "16px";
    toggleText.style.marginLeft = "10px";
    toggleText.style.fontWeight = "bold";
    toggleButton.appendChild(toggleText);

    //====================== Управление высотой панели =======================//
    let isPanelVisible = localStorage.getItem("panelVisible") === "truefalse";

    function togglePanel() {
        if (isPanelVisible) {
            panel.style.height = "0px";
            setTimeout(() => {
                panel.style.display = "none";
            }, 300);
        } else {
            panel.style.display = "block";
            setTimeout(() => {
                panel.style.height = "310px";
            }, 10);
        }
        isPanelVisible = !isPanelVisible;
        localStorage.setItem("panelVisible", isPanelVisible.toString());
    }

    toggleButton.addEventListener("click", togglePanel);

    if (isPanelVisible) {
        panel.style.height = "310px";
        panel.style.display = "block";
    } else {
        panel.style.height = "0px";
        panel.style.display = "none";
    }

    const keywordInput = document.getElementById("keywordInput");
    const addKeywordBtn = document.getElementById("addKeyword");
    const clearKeywordsBtn = document.getElementById("clearKeywords");
    const exportKeywordsBtn = document.getElementById("exportKeywords");
    const importKeywordsBtn = document.getElementById("importKeywords");
    const toggleVerifiedBtn = document.getElementById("toggleVerifiedPosts");
    const toggleNonVerifiedBtn = document.getElementById("toggleNonVerifiedPosts");
    const toggleBlockBtn = document.getElementById("toggleBlockKeywords");
    const openLanguagePopupBtn = document.getElementById("openLanguagePopup");
    const keywordList = document.getElementById("keywordList");

    // ===== Обработчики событий ===== //
    addKeywordBtn.addEventListener("click", () => {
        const keyword = keywordInput.value.trim();
        if (keyword && !hiddenKeywords.includes(keyword)) {
            hiddenKeywords.push(keyword);
            saveKeywords();
            updateKeywordList();
            debouncedHidePosts();
            keywordInput.value = "";
        }
    });

    toggleNonVerifiedBtn.textContent = `Hide non-verified accounts: ${hideNonVerifiedAccounts ? "Turn OFF" : "Turn ON"}`;
    toggleNonVerifiedBtn.addEventListener("click", () => {
        hideNonVerifiedAccounts = !hideNonVerifiedAccounts;
        localStorage.setItem("hideNonVerifiedAccounts", JSON.stringify(hideNonVerifiedAccounts));
        toggleNonVerifiedBtn.textContent = `Hide non-verified accounts: ${hideNonVerifiedAccounts ? "Turn OFF" : "Turn ON"}`;
        hidePosts();
    });

    clearKeywordsBtn.addEventListener("click", () => {
        if (confirm("Are you sure you want to clear the list?")) {
            hiddenKeywords = [];
            unblockedKeywords = [];
            saveKeywords();
            saveUnblockedKeywords();
            updateKeywordList();
            hidePosts();
        }
    });

    exportKeywordsBtn.addEventListener("click", () => {
        const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(hiddenKeywords))}`;
        const downloadAnchor = document.createElement("a");
        downloadAnchor.setAttribute("href", dataStr);
        downloadAnchor.setAttribute("download", "hidden_keywords.json");
        document.body.appendChild(downloadAnchor);
        downloadAnchor.click();
        document.body.removeChild(downloadAnchor);
    });

    importKeywordsBtn.addEventListener("click", () => {
        const input = document.createElement("input");
        input.type = "file";
        input.accept = "application/json";
        input.addEventListener("change", (event) => {
            const file = event.target.files[0];
            const reader = new FileReader();
            reader.onload = () => {
                try {
                    const importedKeywords = JSON.parse(reader.result);
                    if (Array.isArray(importedKeywords)) {
                        hiddenKeywords = [...new Set([...hiddenKeywords, ...importedKeywords])];
                        saveKeywords();
                        updateKeywordList();
                        hidePosts();
                    } else {
                        alert("Incorrect file format.");
                    }
                } catch (e) {
                    alert("Error reading the file.");
                }
            };
            reader.readAsText(file);
        });
        input.click();
    });

    toggleBlockBtn.addEventListener("click", () => {
        if (hiddenKeywords.length > 0) {
            unblockedKeywords = [...hiddenKeywords];
            hiddenKeywords = [];
            toggleBlockBtn.textContent = "Block All";
        } else {
            hiddenKeywords = [...unblockedKeywords];
            unblockedKeywords = [];
            toggleBlockBtn.textContent = "Unblock All";
        }
        saveKeywords();
        saveUnblockedKeywords();
        updateKeywordList();
        hidePosts();
    });

    toggleBlockBtn.textContent = hiddenKeywords.length > 0 ? "Unblock All" : "Block All";

    openLanguagePopupBtn.addEventListener("click", () => {
        const panelRect = panel.getBoundingClientRect();
        languagePopup.style.top = `${panelRect.top - 320}px`;
        languagePopup.style.left = `${panelRect.right - 10}px`;
        languagePopup.style.display = "block";
    });

    let isTranslateButtonClicked = false;
    function handleTranslateButtonClick(event) {
        isTranslateButtonClicked = true;
        setTimeout(() => { isTranslateButtonClicked = false; }, 500);
    }

    function observeTranslateButton() {
        document.querySelectorAll('button[aria-expanded="false"]').forEach(button => {
            button.removeEventListener('click', handleTranslateButtonClick);
            button.addEventListener('click', handleTranslateButtonClick);
        });
    }

    toggleVerifiedBtn.addEventListener("click", () => {
        hideVerifiedAccounts = !hideVerifiedAccounts;
        toggleVerifiedBtn.textContent = `Hide verified accounts: ${hideVerifiedAccounts ? "Turn OFF" : "Turn ON"}`;
        hidePosts();
    });

    // ===== Функция для применения кастомного скроллбара ===== //
    function applyCustomScrollbar() {
        const style = document.createElement("style");
        style.id = "custom-scrollbar-style";
        style.innerHTML = `
            #keywordList::-webkit-scrollbar {
                width: 25px;
            }
            #keywordList::-webkit-scrollbar-thumb {
                background-color: #C1A5EF;
                border-radius: 8px;
                border: 3px solid #4F3E6A;
                height: 80px;
            }
            #keywordList::-webkit-scrollbar-thumb:hover {
                background-color: #C6AEFF;
            }
            #keywordList::-webkit-scrollbar-thumb:active {
                background-color: #B097C9;
            }
            #keywordList::-webkit-scrollbar-track {
                background: #455565;
                border-radius: 0px 0px 8px 0px;
            }
            #keywordList::-webkit-scrollbar-track:hover {
                background-color: #455565;
            }
            #keywordList::-webkit-scrollbar-track:active {
                background-color: #455565;
            }
        `;
        if (!document.getElementById("custom-scrollbar-style")) {
            document.head.appendChild(style);
        }
    }

    // ===== Функция для наблюдения за изменениями в списке ===== //
    function observeKeywordList() {
        const list = document.getElementById("keywordList");
        if (!list) return;

        const observer = new MutationObserver(() => {
            applyCustomScrollbar();
        });
        observer.observe(list, { childList: true, subtree: true });
    }

    // ===== Обновление списка ключевых слов ===== //
    function updateKeywordList() {
        const list = document.getElementById("keywordList");
        list.style.listStyle = "inside";
        list.style.padding = "0px 10px 0px 0px";
        list.style.marginTop = "10px";
        list.style.fontSize = "14px";
        list.style.color = "rgb(255, 255, 255)";
        list.style.maxHeight = "135px";
        list.style.overflowY = "auto";
        list.style.border = "1px solid rgb(204, 204, 204)";
        list.style.borderRadius = "5px";
        list.style.backgroundColor = "rgb(21, 32, 43)";
        list.style.boxShadow = "rgba(0, 0, 0, 0.3) 0px 2px 5px";
        list.style.position = "relative";
        list.style.width = "315px";
        list.innerHTML = "";

        hiddenKeywords.forEach((keyword, index) => {
            const listItem = document.createElement("li");
            listItem.textContent = keyword;
            listItem.style.marginBottom = "5px";

            const deleteButton = document.createElement("button");
            deleteButton.textContent = "❌";
            deleteButton.style.marginLeft = "10px";
            deleteButton.style.backgroundColor = "#f44336";
            deleteButton.style.color = "#fff";
            deleteButton.style.border = "none";
            deleteButton.style.borderRadius = "3px";
            deleteButton.style.cursor = "pointer";
            deleteButton.addEventListener("click", () => {
                hiddenKeywords.splice(index, 1);
                saveKeywords();
                updateKeywordList();
                hidePosts();
            });

            listItem.appendChild(deleteButton);
            list.appendChild(listItem);
        });

        if (hiddenKeywords.length === 0) {
            list.textContent = "Нет";
        }

        applyCustomScrollbar();
        observeKeywordList();
    }

    applyCustomScrollbar();
    observeKeywordList();
    updateKeywordList();
    hidePosts();

    function debounce(func, wait) {
        let timeout;
        return function (...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    }

    const debouncedHidePosts = debounce(hidePosts, 200);

    const observer = new MutationObserver(() => {
        debouncedHidePosts();
        observeTranslateButton();
    });
    observer.observe(document.body, { childList: true, subtree: true });

    // ==== автоматического скрытия панели управления и кнопки переключателя ==== //
    function isPhotoViewerOpen() {
        const currentUrl = window.location.href;
        const isPhotoOpen = /\/photo\/\d+$/.test(currentUrl);
        const photoModal = document.querySelector('div[aria-label="Image"]') || document.querySelector('div[data-testid="imageViewer"]');
        return isPhotoOpen || !!photoModal;
    }

    function updateToggleButtonVisibility() {
        toggleButton.style.display = isGrokPage() ? "none" : "flex";
    }

    function toggleElementsVisibility() {
        const isPhotoOpen = isPhotoViewerOpen();
        if (isPhotoOpen || isGrokPage()) {
            panel.style.display = "none";
            toggleButton.style.display = "none";
        } else {
            if (isPanelVisible) {
                panel.style.display = "block";
                panel.style.height = "310px";
            }
            toggleButton.style.display = "flex";
        }
    }

    toggleElementsVisibility();
    window.addEventListener("popstate", () => {
        updateToggleButtonVisibility();
        toggleElementsVisibility();
    });

    const urlObserver = new MutationObserver(() => {
        updateToggleButtonVisibility();
        toggleElementsVisibility();
    });
    urlObserver.observe(document.body, { childList: true, subtree: true });

    document.addEventListener("click", (event) => {
        if (event.target.closest('div[aria-label="Close"]') || event.target.closest('div[data-testid="imageViewer-close"]')) {
            setTimeout(toggleElementsVisibility, 100);
        }
    });
})();