8chan Style Script

Script to style 8chan

当前为 2025-04-20 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        8chan Style Script
// @namespace   8chanSS
// @match       *://8chan.moe/*/res/*
// @match       *://8chan.se/*/res/*
// @match       *://8chan.cc/*/res/*
// @match       *://8chan.moe/*/catalog.html
// @match       *://8chan.se/*/catalog.html
// @match       *://8chan.cc/*/catalog.html
// @grant       none
// @version     1.20
// @author      OtakuDude
// @run-at      document-idle
// @description Script to style 8chan
// @license     MIT
// ==/UserScript==
(function () {
    // --- Settings ---
    const scriptSettings = {
        // Organize settings by category
        site: {
            enableHeaderCatalogLinks: {
                label: "Header Catalog Links",
                default: true,
                subOptions: {
                    openInNewTab: { label: "Always open in new tab", default: false }
                }
            },
            enableBottomHeader: { label: "Bottom Header", default: false },
            enableScrollSave: { label: "Save Scroll Position", default: true },
            enableScrollArrows: { label: "Show Up/Down Arrows", default: false },
            hoverVideoVolume: { label: "Hover Video Volume (0-100%)", default: 50, type: "number", min: 0, max: 100 },
        },
        threads: {
            beepOnYou: { label: "Beep on (You)", default: false },
            notifyOnYou: { label: "Notify when (You) (!)", default: true },
            blurSpoilers: {
                label: "Blur Spoilers",
                default: false,
                subOptions: {
                    removeSpoilers: { label: "Remove Spoilers", default: false }
                }
            },
            enableSaveName: { label: "Save Name Checkbox", default: true },
            enableThreadImageHover: { label: "Thread Image Hover", default: true },
        },
        catalog: {
            enableCatalogImageHover: { label: "Catalog Image Hover", default: true },
        },
        styling: {
            enableStickyQR: { label: "Enable Sticky Quick Reply", default: false },
            enableFitReplies: { label: "Fit Replies", default: false },
            enableSidebar: { label: "Enable Sidebar", default: false },
            hideAnnouncement: { label: "Hide Announcement", default: false },
            hidePanelMessage: { label: "Hide Panel Message", default: false },
            hidePostingForm: { label: "Hide Posting Form", default: false },
            hideBanner: { label: "Hide Board Banners", default: false },
        }
    };

    // Flatten settings for backward compatibility with existing functions
    const flatSettings = {};
    function flattenSettings() {
        Object.keys(scriptSettings).forEach(category => {
            Object.keys(scriptSettings[category]).forEach(key => {
                flatSettings[key] = scriptSettings[category][key];

                // Also flatten any sub-options
                if (scriptSettings[category][key].subOptions) {
                    Object.keys(scriptSettings[category][key].subOptions).forEach(subKey => {
                        const fullKey = `${key}_${subKey}`;
                        flatSettings[fullKey] = scriptSettings[category][key].subOptions[subKey];
                    });
                }
            });
        });
    }
    flattenSettings();

    function getSetting(key) {
        // Check if the key exists in flatSettings
        if (!flatSettings[key]) {
            console.warn(`Setting key not found: ${key}`);
            return false; // Default to false for unknown settings
        }

        const val = localStorage.getItem('8chanSS_' + key);
        if (val === null) return flatSettings[key].default;
        if (flatSettings[key].type === "number") return Number(val);
        return val === 'true';
    }

    function setSetting(key, value) {
        localStorage.setItem('8chanSS_' + key, value);
    }

    // --- Menu Icon ---
    const themeSelector = document.getElementById('themesBefore');
    let link = null;
    let bracketSpan = null;
    if (themeSelector) {
        bracketSpan = document.createElement('span');
        bracketSpan.textContent = '] [ ';
        link = document.createElement('a');
        link.id = '8chanSS-icon';
        link.href = '#';
        link.textContent = '8chanSS';

        themeSelector.parentNode.insertBefore(bracketSpan, themeSelector.nextSibling);
        themeSelector.parentNode.insertBefore(link, bracketSpan.nextSibling);
    }

    // --- Floating Settings Menu with Tabs ---
    function createSettingsMenu() {
        let menu = document.getElementById('8chanSS-menu');
        if (menu) return menu;
        menu = document.createElement('div');
        menu.id = '8chanSS-menu';
        menu.style.position = 'fixed';
        menu.style.top = '80px';
        menu.style.left = '30px';
        menu.style.zIndex = 99999;
        menu.style.background = '#222';
        menu.style.color = '#fff';
        menu.style.padding = '0';
        menu.style.borderRadius = '8px';
        menu.style.boxShadow = '0 4px 16px rgba(0,0,0,0.25)';
        menu.style.display = 'none';
        menu.style.minWidth = '220px';
        menu.style.width = '100%';
        menu.style.maxWidth = '350px';
        menu.style.fontFamily = 'sans-serif';
        menu.style.userSelect = 'none';

        // Draggable
        let isDragging = false, dragOffsetX = 0, dragOffsetY = 0;
        const header = document.createElement('div');
        header.style.display = 'flex';
        header.style.justifyContent = 'space-between';
        header.style.alignItems = 'center';
        header.style.marginBottom = '0';
        header.style.cursor = 'move';
        header.style.background = '#333';
        header.style.padding = '5px 18px 5px';
        header.style.borderTopLeftRadius = '8px';
        header.style.borderTopRightRadius = '8px';
        header.addEventListener('mousedown', function (e) {
            isDragging = true;
            const rect = menu.getBoundingClientRect();
            dragOffsetX = e.clientX - rect.left;
            dragOffsetY = e.clientY - rect.top;
            document.body.style.userSelect = 'none';
        });
        document.addEventListener('mousemove', function (e) {
            if (!isDragging) return;
            let newLeft = e.clientX - dragOffsetX;
            let newTop = e.clientY - dragOffsetY;
            const menuRect = menu.getBoundingClientRect();
            const menuWidth = menuRect.width;
            const menuHeight = menuRect.height;
            const viewportWidth = window.innerWidth;
            const viewportHeight = window.innerHeight;
            newLeft = Math.max(0, Math.min(newLeft, viewportWidth - menuWidth));
            newTop = Math.max(0, Math.min(newTop, viewportHeight - menuHeight));
            menu.style.left = newLeft + 'px';
            menu.style.top = newTop + 'px';
            menu.style.right = 'auto';
        });
        document.addEventListener('mouseup', function () {
            isDragging = false;
            document.body.style.userSelect = '';
        });

        // Title and close button
        const title = document.createElement('span');
        title.textContent = '8chanSS Settings';
        title.style.fontWeight = 'bold';
        header.appendChild(title);

        const closeBtn = document.createElement('button');
        closeBtn.textContent = '✕';
        closeBtn.style.background = 'none';
        closeBtn.style.border = 'none';
        closeBtn.style.color = '#fff';
        closeBtn.style.fontSize = '18px';
        closeBtn.style.cursor = 'pointer';
        closeBtn.style.marginLeft = '10px';
        closeBtn.addEventListener('click', () => {
            menu.style.display = 'none';
        });
        header.appendChild(closeBtn);

        menu.appendChild(header);

        // Tab navigation
        const tabNav = document.createElement('div');
        tabNav.style.display = 'flex';
        tabNav.style.borderBottom = '1px solid #444';
        tabNav.style.background = '#2a2a2a';

        // Tab content container
        const tabContent = document.createElement('div');
        tabContent.style.padding = '15px 18px';
        tabContent.style.maxHeight = '60vh';
        tabContent.style.overflowY = 'auto';

        // Store current (unsaved) values
        const tempSettings = {};
        Object.keys(flatSettings).forEach(key => {
            tempSettings[key] = getSetting(key);
        });

        // Create tabs
        const tabs = {
            site: { label: 'Site', content: createTabContent('site', tempSettings) },
            threads: { label: 'Threads', content: createTabContent('threads', tempSettings) },
            catalog: { label: 'Catalog', content: createTabContent('catalog', tempSettings) },
            styling: { label: 'Styling', content: createTabContent('styling', tempSettings) }
        };

        // Create tab buttons
        Object.keys(tabs).forEach((tabId, index, arr) => {
            const tab = tabs[tabId];
            const tabButton = document.createElement('button');
            tabButton.textContent = tab.label;
            tabButton.dataset.tab = tabId;
            tabButton.style.background = index === 0 ? '#333' : 'transparent';
            tabButton.style.border = 'none';
            tabButton.style.borderRight = '1px solid #444';
            tabButton.style.color = '#fff';
            tabButton.style.padding = '8px 15px';
            tabButton.style.margin = '5px 0 0 0';
            tabButton.style.cursor = 'pointer';
            tabButton.style.flex = '1';
            tabButton.style.fontSize = '14px';
            tabButton.style.transition = 'background 0.2s';

            // Add rounded corners and margin to the first and last tab
            if (index === 0) {
                tabButton.style.borderTopLeftRadius = '8px';
                tabButton.style.margin = '5px 0 0 5px';
            }
            if (index === arr.length - 1) {
                tabButton.style.borderTopRightRadius = '8px';
                tabButton.style.margin = '5px 5px 0 0';
                tabButton.style.borderRight = 'none'; // Remove border on last tab
            }

            tabButton.addEventListener('click', () => {
                // Hide all tab contents
                Object.values(tabs).forEach(t => {
                    t.content.style.display = 'none';
                });

                // Show selected tab content
                tab.content.style.display = 'block';

                // Update active tab button
                tabNav.querySelectorAll('button').forEach(btn => {
                    btn.style.background = 'transparent';
                });
                tabButton.style.background = '#333';
            });

            tabNav.appendChild(tabButton);
        });

        menu.appendChild(tabNav);

        // Add all tab contents to the container
        Object.values(tabs).forEach((tab, index) => {
            tab.content.style.display = index === 0 ? 'block' : 'none';
            tabContent.appendChild(tab.content);
        });

        menu.appendChild(tabContent);

        // Button container for Save and Reset buttons
        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.gap = '10px';
        buttonContainer.style.padding = '0 18px 15px';

        // Save Button
        const saveBtn = document.createElement('button');
        saveBtn.textContent = 'Save';
        saveBtn.style.background = '#4caf50';
        saveBtn.style.color = '#fff';
        saveBtn.style.border = 'none';
        saveBtn.style.borderRadius = '4px';
        saveBtn.style.padding = '8px 18px';
        saveBtn.style.fontSize = '15px';
        saveBtn.style.cursor = 'pointer';
        saveBtn.style.flex = '1';
        saveBtn.addEventListener('click', function () {
            Object.keys(tempSettings).forEach(key => {
                setSetting(key, tempSettings[key]);
            });
            saveBtn.textContent = 'Saved!';
            setTimeout(() => { saveBtn.textContent = 'Save'; }, 900);
            setTimeout(() => { window.location.reload(); }, 400);
        });
        buttonContainer.appendChild(saveBtn);

        // Reset Button
        const resetBtn = document.createElement('button');
        resetBtn.textContent = 'Reset';
        resetBtn.style.background = '#dd3333';
        resetBtn.style.color = '#fff';
        resetBtn.style.border = 'none';
        resetBtn.style.borderRadius = '4px';
        resetBtn.style.padding = '8px 18px';
        resetBtn.style.fontSize = '15px';
        resetBtn.style.cursor = 'pointer';
        resetBtn.style.flex = '1';
        resetBtn.addEventListener('click', function () {
            if (confirm('Reset all 8chanSS settings to defaults?')) {
                // Find and remove all 8chanSS_ localStorage items
                Object.keys(localStorage).forEach(key => {
                    if (key.startsWith('8chanSS_')) {
                        localStorage.removeItem(key);
                    }
                });
                resetBtn.textContent = 'Reset!';
                setTimeout(() => { resetBtn.textContent = 'Reset'; }, 900);
                setTimeout(() => { window.location.reload(); }, 400);
            }
        });
        buttonContainer.appendChild(resetBtn);

        menu.appendChild(buttonContainer);

        // Info
        const info = document.createElement('div');
        info.style.fontSize = '11px';
        info.style.padding = '0 18px 12px';
        info.style.opacity = '0.7';
        info.style.textAlign = 'center';
        info.textContent = 'Press Save to apply changes. Page will reload.';
        menu.appendChild(info);

        document.body.appendChild(menu);
        return menu;
    }

    // Helper function to create tab content
    function createTabContent(category, tempSettings) {
        const container = document.createElement('div');
        const categorySettings = scriptSettings[category];

        Object.keys(categorySettings).forEach(key => {
            const setting = categorySettings[key];

            // Parent row: flex for checkbox, label, chevron
            const parentRow = document.createElement('div');
            parentRow.style.display = 'flex';
            parentRow.style.alignItems = 'center';
            parentRow.style.marginBottom = '0px';

            // Special case: hoverVideoVolume slider
            if (key === "hoverVideoVolume" && setting.type === "number") {
                const label = document.createElement('label');
                label.htmlFor = 'setting_' + key;
                label.textContent = setting.label + ': ';
                label.style.flex = '1';

                const sliderContainer = document.createElement('div');
                sliderContainer.style.display = 'flex';
                sliderContainer.style.alignItems = 'center';
                sliderContainer.style.flex = '1';

                const slider = document.createElement('input');
                slider.type = 'range';
                slider.id = 'setting_' + key;
                slider.min = setting.min;
                slider.max = setting.max;
                slider.value = tempSettings[key];
                slider.style.flex = 'unset';
                slider.style.width = '100px';
                slider.style.marginRight = '10px';

                const valueLabel = document.createElement('span');
                valueLabel.textContent = slider.value + '%';
                valueLabel.style.minWidth = '40px';
                valueLabel.style.textAlign = 'right';

                slider.addEventListener('input', function () {
                    let val = Number(slider.value);
                    if (isNaN(val)) val = setting.default;
                    val = Math.max(setting.min, Math.min(setting.max, val));
                    slider.value = val;
                    tempSettings[key] = val;
                    valueLabel.textContent = val + '%';
                });

                sliderContainer.appendChild(slider);
                sliderContainer.appendChild(valueLabel);

                parentRow.appendChild(label);
                parentRow.appendChild(sliderContainer);

                // Wrapper for parent row and sub-options
                const wrapper = document.createElement('div');
                wrapper.style.marginBottom = '10px';
                wrapper.appendChild(parentRow);
                container.appendChild(wrapper);
                return; // Skip the rest for this key
            }

            // Checkbox for boolean settings
            const checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.id = 'setting_' + key;
            checkbox.checked = tempSettings[key];
            checkbox.style.marginRight = '8px';

            // Label
            const label = document.createElement('label');
            label.htmlFor = checkbox.id;
            label.textContent = setting.label;
            label.style.flex = '1';

            // Chevron for subOptions
            let chevron = null;
            let subOptionsContainer = null;
            if (setting.subOptions) {
                chevron = document.createElement('span');
                chevron.className = 'ss-chevron';
                chevron.innerHTML = '▶'; // Right-pointing triangle
                chevron.style.display = 'inline-block';
                chevron.style.transition = 'transform 0.2s';
                chevron.style.marginLeft = '6px';
                chevron.style.fontSize = '12px';
                chevron.style.userSelect = 'none';
                chevron.style.transform = checkbox.checked ? 'rotate(90deg)' : 'rotate(0deg)';
            }

            // Checkbox change handler
            checkbox.addEventListener('change', function () {
                tempSettings[key] = checkbox.checked;
                if (setting.subOptions && subOptionsContainer) {
                    subOptionsContainer.style.display = checkbox.checked ? 'block' : 'none';
                    if (chevron) {
                        chevron.style.transform = checkbox.checked ? 'rotate(90deg)' : 'rotate(0deg)';
                    }
                }
            });

            parentRow.appendChild(checkbox);
            parentRow.appendChild(label);
            if (chevron) parentRow.appendChild(chevron);

            // Wrapper for parent row and sub-options
            const wrapper = document.createElement('div');
            wrapper.style.marginBottom = '10px';

            wrapper.appendChild(parentRow);

            // Handle sub-options if any exist
            if (setting.subOptions) {
                subOptionsContainer = document.createElement('div');
                subOptionsContainer.style.marginLeft = '25px';
                subOptionsContainer.style.marginTop = '5px';
                subOptionsContainer.style.display = checkbox.checked ? 'block' : 'none';

                Object.keys(setting.subOptions).forEach(subKey => {
                    const subSetting = setting.subOptions[subKey];
                    const fullKey = `${key}_${subKey}`;

                    const subWrapper = document.createElement('div');
                    subWrapper.style.marginBottom = '5px';

                    const subCheckbox = document.createElement('input');
                    subCheckbox.type = 'checkbox';
                    subCheckbox.id = 'setting_' + fullKey;
                    subCheckbox.checked = tempSettings[fullKey];
                    subCheckbox.style.marginRight = '8px';

                    subCheckbox.addEventListener('change', function () {
                        tempSettings[fullKey] = subCheckbox.checked;
                    });

                    const subLabel = document.createElement('label');
                    subLabel.htmlFor = subCheckbox.id;
                    subLabel.textContent = subSetting.label;

                    subWrapper.appendChild(subCheckbox);
                    subWrapper.appendChild(subLabel);
                    subOptionsContainer.appendChild(subWrapper);
                });

                wrapper.appendChild(subOptionsContainer);
            }

            container.appendChild(wrapper);
        });

        // Add minimal CSS for chevron (only once)
        if (!document.getElementById('ss-chevron-style')) {
            const style = document.createElement('style');
            style.id = 'ss-chevron-style';
            style.textContent = `
                .ss-chevron {
                    transition: transform 0.2s;
                    margin-left: 6px;
                    font-size: 12px;
                    display: inline-block;
                }
            `;
            document.head.appendChild(style);
        }

        return container;
    }

    // Hook up the icon to open/close the menu
    if (link) {
        let menu = createSettingsMenu();
        link.style.cursor = 'pointer';
        link.title = 'Open 8chanSS settings';
        link.addEventListener('click', function (e) {
            e.preventDefault();
            menu = createSettingsMenu();
            menu.style.display = (menu.style.display === 'none') ? 'block' : 'none';
        });
    }

    /* --- Scroll Arrows Feature --- */
    function featureScrollArrows() {
        // Only add once
        if (document.getElementById('scroll-arrow-up') || document.getElementById('scroll-arrow-down')) return;

        // Up arrow
        const upBtn = document.createElement('button');
        upBtn.id = 'scroll-arrow-up';
        upBtn.className = 'scroll-arrow-btn';
        upBtn.title = 'Scroll to top';
        upBtn.innerHTML = '▲';
        upBtn.addEventListener('click', () => {
            window.scrollTo({ top: 0, behavior: 'smooth' });
        });

        // Down arrow
        const downBtn = document.createElement('button');
        downBtn.id = 'scroll-arrow-down';
        downBtn.className = 'scroll-arrow-btn';
        downBtn.title = 'Scroll to bottom';
        downBtn.innerHTML = '▼';
        downBtn.addEventListener('click', () => {
            const footer = document.getElementById('footer');
            if (footer) {
                footer.scrollIntoView({ behavior: 'smooth', block: 'end' });
            } else {
                window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
            }
        });

        document.body.appendChild(upBtn);
        document.body.appendChild(downBtn);
    }

    // --- Feature: Beep on (You) ---
    function featureBeepOnYou() {
        // Beep sound (base64)
        const beep = new Audio('data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA');

        // Store the original title
        const originalTitle = document.title;
        let isNotifying = false;

        // Create MutationObserver to detect when you are quoted
        const observer = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === 1 && node.querySelector && node.querySelector('a.quoteLink.you')) {
                        // Only play beep if the setting is enabled
                        if (getSetting('beepOnYou')) {
                            playBeep();
                        }

                        // Trigger notification in separate function if enabled
                        if (getSetting('notifyOnYou')) {
                            featureNotifyOnYou();
                        }
                    }
                });
            });
        });

        observer.observe(document.body, { childList: true, subtree: true });

        // Function to play the beep sound
        function playBeep() {
            if (beep.paused) {
                beep.play().catch(e => console.warn("Beep failed:", e));
            } else {
                beep.addEventListener('ended', () => beep.play(), { once: true });
            }
        }
        // Function to notify on (You)
        function featureNotifyOnYou() {
            // Store the original title if not already stored
            if (!window.originalTitle) {
                window.originalTitle = document.title;
            }

            // Add notification to title if not already notifying and tab not focused
            if (!window.isNotifying && !document.hasFocus()) {
                window.isNotifying = true;
                document.title = "(!) " + window.originalTitle;

                // Set up focus event listener if not already set
                if (!window.notifyFocusListenerAdded) {
                    window.addEventListener('focus', () => {
                        if (window.isNotifying) {
                            document.title = window.originalTitle;
                            window.isNotifying = false;
                        }
                    });
                    window.notifyFocusListenerAdded = true;
                }
            }
        }
        // Function to add notification to the title
        function addNotificationToTitle() {
            if (!isNotifying && !document.hasFocus()) {
                isNotifying = true;
                document.title = "(!) " + originalTitle;
            }
        }
        // Remove notification when tab regains focus
        window.addEventListener('focus', () => {
            if (isNotifying) {
                document.title = originalTitle;
                isNotifying = false;
            }
        });
    }

    // --- Feature: Header Catalog Links ---
    function featureHeaderCatalogLinks() {
        function appendCatalogToLinks() {
            const navboardsSpan = document.getElementById('navBoardsSpan');
            if (navboardsSpan) {
                const links = navboardsSpan.getElementsByTagName('a');
                const openInNewTab = getSetting('enableHeaderCatalogLinks_openInNewTab');

                for (let link of links) {
                    if (link.href && !link.href.endsWith('/catalog.html')) {
                        link.href += '/catalog.html';

                        // Set target="_blank" if the option is enabled
                        if (openInNewTab) {
                            link.target = '_blank';
                            link.rel = 'noopener noreferrer'; // Security best practice
                        } else {
                            link.target = '';
                            link.rel = '';
                        }
                    }
                }
            }
        }

        appendCatalogToLinks();
        const observer = new MutationObserver(appendCatalogToLinks);
        const config = { childList: true, subtree: true };
        const navboardsSpan = document.getElementById('navBoardsSpan');
        if (navboardsSpan) {
            observer.observe(navboardsSpan, config);
        }
    }

    // --- Feature: Save Scroll Position ---
    function featureSaveScrollPosition() {
        const MAX_PAGES = 50;
        const currentPage = window.location.href;
        const excludedPagePatterns = [
            /\/catalog\.html$/i,
        ];
        function isExcludedPage(url) {
            return excludedPagePatterns.some(pattern => pattern.test(url));
        }
        function saveScrollPosition() {
            if (isExcludedPage(currentPage)) return;
            const scrollPosition = window.scrollY;
            localStorage.setItem(`8chanSS_scrollPosition_${currentPage}`, scrollPosition);
            manageScrollStorage();
        }
        function restoreScrollPosition() {
            // If the URL contains a hash (e.g. /res/1190.html#1534), do nothing
            if (window.location.hash && window.location.hash.length > 1) {
                // There is a hash fragment, skip restoring scroll position
                return;
            }
            const savedPosition = localStorage.getItem(`8chanSS_scrollPosition_${currentPage}`);
            if (savedPosition) {
                window.scrollTo(0, parseInt(savedPosition, 10));
            }
        }
        function manageScrollStorage() {
            const keys = Object.keys(localStorage).filter(key => key.startsWith('8chanSS_scrollPosition_'));
            if (keys.length > MAX_PAGES) {
                keys.sort((a, b) => {
                    return localStorage.getItem(a) - localStorage.getItem(b);
                });
                while (keys.length > MAX_PAGES) {
                    localStorage.removeItem(keys.shift());
                }
            }
        }
        window.addEventListener('beforeunload', saveScrollPosition);
        window.addEventListener('load', restoreScrollPosition);
    }

    // --- Feature: Catalog & Image Hover ---
    function featureImageHover() {
        function getFullMediaSrcFromMime(thumbnailSrc, filemime) {
            if (!thumbnailSrc || !filemime) return null;
            let base = thumbnailSrc.replace(/\/t_/, '/');
            base = base.replace(/\.(jpe?g|png|gif|webp|webm|mp4)$/i, '');
            const mimeToExt = {
                'image/jpeg': '.jpg',
                'image/jpg': '.jpg',
                'image/png': '.png',
                'image/gif': '.gif',
                'image/webp': '.webp',
                'video/mp4': '.mp4',
                'video/webm': '.webm'
            };
            const ext = mimeToExt[filemime.toLowerCase()];
            if (!ext) return null;
            return base + ext;
        }

        let floatingMedia = null;
        let removeListeners = null;
        let hoverTimeout = null;
        let lastThumb = null;
        let isStillHovering = false;

        function cleanupFloatingMedia() {
            if (hoverTimeout) {
                clearTimeout(hoverTimeout);
                hoverTimeout = null;
            }
            if (removeListeners) {
                removeListeners();
                removeListeners = null;
            }
            if (floatingMedia) {
                if (floatingMedia.tagName === 'VIDEO') {
                    try {
                        floatingMedia.pause();
                        floatingMedia.removeAttribute('src');
                        floatingMedia.load();
                    } catch (e) { }
                }
                if (floatingMedia.parentNode) {
                    floatingMedia.parentNode.removeChild(floatingMedia);
                }
            }
            floatingMedia = null;
            lastThumb = null;
            isStillHovering = false;
            document.removeEventListener('mousemove', onMouseMove);
        }

        function onMouseMove(event) {
            if (!floatingMedia) return;
            const viewportWidth = window.innerWidth;
            const viewportHeight = window.innerHeight;
            let mediaWidth = 0, mediaHeight = 0;
            if (floatingMedia.tagName === 'IMG') {
                mediaWidth = floatingMedia.naturalWidth || floatingMedia.width || floatingMedia.offsetWidth || 0;
                mediaHeight = floatingMedia.naturalHeight || floatingMedia.height || floatingMedia.offsetHeight || 0;
            } else if (floatingMedia.tagName === 'VIDEO') {
                mediaWidth = floatingMedia.videoWidth || floatingMedia.offsetWidth || 0;
                mediaHeight = floatingMedia.videoHeight || floatingMedia.offsetHeight || 0;
            }
            mediaWidth = Math.min(mediaWidth, viewportWidth * 0.9);
            mediaHeight = Math.min(mediaHeight, viewportHeight * 0.9);
            let newX = event.clientX + 10;
            let newY = event.clientY + 10;
            if (newX + mediaWidth > viewportWidth) {
                newX = viewportWidth - mediaWidth - 10;
            }
            if (newY + mediaHeight > viewportHeight) {
                newY = viewportHeight - mediaHeight - 10;
            }
            newX = Math.max(newX, 0);
            newY = Math.max(newY, 0);
            floatingMedia.style.left = `${newX}px`;
            floatingMedia.style.top = `${newY}px`;
            floatingMedia.style.maxWidth = '90vw';
            floatingMedia.style.maxHeight = '90vh';
        }

        function onThumbEnter(e) {
            const thumb = e.currentTarget;
            // Debounce: if already hovering this thumb, do nothing
            if (lastThumb === thumb) return;
            lastThumb = thumb;

            // Clean up any previous floating media and debounce
            cleanupFloatingMedia();

            isStillHovering = true;

            // Listen for mouseleave to cancel hover if left before timeout
            function onLeave() {
                isStillHovering = false;
                cleanupFloatingMedia();
            }
            thumb.addEventListener('mouseleave', onLeave, { once: true });

            // Debounce: wait a short time before showing preview
            hoverTimeout = setTimeout(() => {
                hoverTimeout = null;
                // If mouse has left before timeout, do not show preview
                if (!isStillHovering) return;

                const parentA = thumb.closest('a.linkThumb, a.imgLink');
                if (!parentA) return;
                const filemime = parentA.getAttribute('data-filemime');
                const fullSrc = getFullMediaSrcFromMime(thumb.getAttribute('src'), filemime);
                if (!fullSrc) return;

                let loaded = false;
                function setCommonStyles(el) {
                    el.style.position = 'fixed';
                    el.style.zIndex = 9999;
                    el.style.pointerEvents = 'none';
                    el.style.maxWidth = '95vw';
                    el.style.maxHeight = '95vh';
                    el.style.transition = 'opacity 0.15s';
                    el.style.opacity = '0';
                    el.style.left = '-9999px';
                }

                // Setup cleanup listeners
                removeListeners = function () {
                    window.removeEventListener('scroll', cleanupFloatingMedia, true);
                };
                window.addEventListener('scroll', cleanupFloatingMedia, true);

                if (filemime && filemime.startsWith('image/')) {
                    floatingMedia = document.createElement('img');
                    setCommonStyles(floatingMedia);
                    floatingMedia.onload = function () {
                        if (!loaded && floatingMedia && isStillHovering) {
                            loaded = true;
                            floatingMedia.style.opacity = '1';
                            document.body.appendChild(floatingMedia);
                            document.addEventListener('mousemove', onMouseMove);
                            onMouseMove(e);
                        }
                    };
                    floatingMedia.onerror = cleanupFloatingMedia;
                    floatingMedia.src = fullSrc;
                } else if (filemime && filemime.startsWith('video/')) {
                    floatingMedia = document.createElement('video');
                    setCommonStyles(floatingMedia);
                    floatingMedia.autoplay = true;
                    floatingMedia.loop = true;
                    floatingMedia.muted = false;
                    floatingMedia.playsInline = true;
                    // Set volume from settings (0-100)
                    let volume = typeof getSetting === "function" ? getSetting('hoverVideoVolume') : 50;
                    if (typeof volume !== 'number' || isNaN(volume)) volume = 50;
                    floatingMedia.volume = Math.max(0, Math.min(1, volume / 100));
                    floatingMedia.onloadeddata = function () {
                        if (!loaded && floatingMedia && isStillHovering) {
                            loaded = true;
                            floatingMedia.style.opacity = '1';
                            document.body.appendChild(floatingMedia);
                            document.addEventListener('mousemove', onMouseMove);
                            onMouseMove(e);
                        }
                    };
                    floatingMedia.onerror = cleanupFloatingMedia;
                    floatingMedia.src = fullSrc;
                }
            }, 120); // 120ms debounce for both images and videos
        }

        function attachThumbListeners(root) {
            const thumbs = (root || document).querySelectorAll('a.linkThumb > img, a.imgLink > img');
            thumbs.forEach(thumb => {
                if (!thumb._fullImgHoverBound) {
                    thumb.addEventListener('mouseenter', onThumbEnter);
                    thumb._fullImgHoverBound = true;
                }
            });
        }

        attachThumbListeners();
        const observer = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        attachThumbListeners(node);
                    }
                });
            });
        });
        observer.observe(document.body, { childList: true, subtree: true });
    }

    // --- Feature: Save Name Checkbox ---
    function featureSaveNameCheckbox() {
        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.id = 'saveNameCheckbox';
        checkbox.classList.add('postingCheckbox');
        const label = document.createElement('label');
        label.htmlFor = 'saveNameCheckbox';
        label.textContent = 'Save Name';
        label.title = 'Save Name on refresh';
        const alwaysUseBypassCheckbox = document.getElementById('qralwaysUseBypassCheckBox');
        if (alwaysUseBypassCheckbox) {
            alwaysUseBypassCheckbox.parentNode.insertBefore(checkbox, alwaysUseBypassCheckbox);
            alwaysUseBypassCheckbox.parentNode.insertBefore(label, checkbox.nextSibling);
            const savedCheckboxState = localStorage.getItem('8chanSS_saveNameCheckbox') === 'true';
            checkbox.checked = savedCheckboxState;
            const nameInput = document.getElementById('qrname');
            if (nameInput) {
                const savedName = localStorage.getItem('name');
                if (checkbox.checked && savedName !== null) {
                    nameInput.value = savedName;
                } else if (!checkbox.checked) {
                    nameInput.value = '';
                }
                nameInput.addEventListener('input', function () {
                    if (checkbox.checked) {
                        localStorage.setItem('name', nameInput.value);
                    }
                });
                checkbox.addEventListener('change', function () {
                    if (checkbox.checked) {
                        localStorage.setItem('name', nameInput.value);
                    } else {
                        localStorage.removeItem('name');
                        nameInput.value = '';
                    }
                    localStorage.setItem('8chanSS_saveNameCheckbox', checkbox.checked);
                });
            }
        }
    }

    /* --- Feature: Blur Spoilers + Remove Spoilers suboption --- */
    function featureBlurSpoilers() {
        function revealSpoilers() {
            const spoilerLinks = document.querySelectorAll('a.imgLink');
            spoilerLinks.forEach(link => {
                const img = link.querySelector('img');
                if (img && !img.src.includes('/.media/t_')) {
                    let href = link.getAttribute('href');
                    if (href) {
                        // Extract filename without extension
                        const match = href.match(/\/\.media\/([^\/]+)\.[a-zA-Z0-9]+$/);
                        if (match) {
                            // Use the thumbnail path (t_filename)
                            const transformedSrc = `/\.media/t_${match[1]}`;
                            img.src = transformedSrc;

                            // If Remove Spoilers is enabled, do not apply blur, just show the thumbnail
                            if (getSetting('blurSpoilers_removeSpoilers')) {
                                img.style.filter = '';
                                img.style.transition = '';
                                img.onmouseover = null;
                                img.onmouseout = null;
                                return;
                            } else {
                                img.style.filter = 'blur(5px)';
                                img.style.transition = 'filter 0.3s ease';
                                img.addEventListener('mouseover', () => {
                                    img.style.filter = 'none';
                                });
                                img.addEventListener('mouseout', () => {
                                    img.style.filter = 'blur(5px)';
                                });
                            }
                        }
                    }
                }
            });
        }

        // Initial run
        revealSpoilers();

        // Observe for dynamically added spoilers
        const observer = new MutationObserver(revealSpoilers);
        observer.observe(document.body, { childList: true, subtree: true });
    }

    // --- Feature: CSS Class Toggles ---
    function featureCssClassToggles() {
        // Map of setting keys to CSS class names
        const classToggles = {
            'enableFitReplies': 'fit-replies',
            'enableSidebar': 'ss-sidebar',
            'enableStickyQR': 'sticky-qr',
            'enableBottomHeader': 'bottom-header',
            'hideBanner': 'disable-banner'
            // Add more class toggles here in the future
        };

        // Process each toggle
        Object.entries(classToggles).forEach(([settingKey, className]) => {
            if (getSetting(settingKey)) {
                document.documentElement.classList.add(className);
            } else {
                document.documentElement.classList.remove(className);
            }
        });
    }

    // --- Feature: Hide/Show Posting Form, Announcement, Panel Message ---
    function featureHideElements() {
        // These settings are: hidePostingForm, hideAnnouncement, hidePanelMessage
        const postingFormDiv = document.getElementById('postingForm');
        const announcementDiv = document.getElementById('dynamicAnnouncement');
        const panelMessageDiv = document.getElementById('panelMessage');
        if (postingFormDiv) {
            postingFormDiv.style.display = getSetting('hidePostingForm') ? 'none' : '';
        }
        if (announcementDiv) {
            announcementDiv.style.display = getSetting('hideAnnouncement') ? 'none' : '';
        }
        if (panelMessageDiv) {
            panelMessageDiv.style.display = getSetting('hidePanelMessage') ? 'none' : '';
        }
    }

    // --- Feature Initialization based on Settings ---
    if (getSetting('blurSpoilers')) {
        featureBlurSpoilers();
    }
    if (getSetting('enableHeaderCatalogLinks')) {
        featureHeaderCatalogLinks();
    }
    if (getSetting('enableScrollSave')) {
        featureSaveScrollPosition();
    }
    if (getSetting('enableSaveName')) {
        featureSaveNameCheckbox();
    }
    if (getSetting('enableScrollArrows')) {
        featureScrollArrows();
    }
    if (getSetting('beepOnYou') || getSetting('notifyOnYou')) {
        featureBeepOnYou();
    }

    // Check if we should enable image hover based on the current page
    const isCatalogPage = /\/catalog\.html$/.test(window.location.pathname.toLowerCase());
    if ((isCatalogPage && getSetting('enableCatalogImageHover')) ||
        (!isCatalogPage && getSetting('enableThreadImageHover'))) {
        featureImageHover();
    }

    // Always run hide/show feature (it will respect settings)
    featureHideElements();
    featureCssClassToggles();

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // Keyboard Shortcuts
    // QR (CTRL+Q)
    function toggleQR(event) {
        // Check if Ctrl + Q is pressed
        if (event.ctrlKey && (event.key === 'q' || event.key === 'Q')) {
            const hiddenDiv = document.getElementById('quick-reply');
            // Toggle QR
            if (hiddenDiv.style.display === 'none' || hiddenDiv.style.display === '') {
                hiddenDiv.style.display = 'block'; // Show the div

                // Focus the textarea after a small delay to ensure it's visible
                setTimeout(() => {
                    const textarea = document.getElementById('qrbody');
                    if (textarea) {
                        textarea.focus();
                    }
                }, 50);
            }
            else {
                hiddenDiv.style.display = 'none'; // Hide the div
            }
        }
    }
    document.addEventListener('keydown', toggleQR);

    // Clear textarea and hide quick-reply on Escape key
    function clearTextarea(event) {
        // Check if Escape key is pressed
        if (event.key === 'Escape') {
            // Clear the textarea
            const textarea = document.getElementById('qrbody');
            if (textarea) {
                textarea.value = ''; // Clear the textarea
            }

            // Hide the quick-reply div
            const quickReply = document.getElementById('quick-reply');
            if (quickReply) {
                quickReply.style.display = 'none'; // Hide the quick-reply
            }
        }
    }
    document.addEventListener('keydown', clearTextarea);

    // Tags
    const bbCodeCombinations = new Map([
        ["s", ["[spoiler]", "[/spoiler]"]],
        ["b", ["'''", "'''"]],
        ["u", ["__", "__"]],
        ["i", ["''", "''"]],
        ["d", ["[doom]", "[/doom]"]],
        ["m", ["[moe]", "[/moe]"]],
        ["c", ["[code]", "[/code]"]],
    ]);

    function replyKeyboardShortcuts(ev) {
        const key = ev.key.toLowerCase();
        // Special case: alt+c for [code] tag
        if (key === "c" && ev.altKey && !ev.ctrlKey && bbCodeCombinations.has(key)) {
            ev.preventDefault();
            const textBox = ev.target;
            const [openTag, closeTag] = bbCodeCombinations.get(key);
            const { selectionStart, selectionEnd, value } = textBox;
            if (selectionStart === selectionEnd) {
                // No selection: insert empty tags and place cursor between them
                const before = value.slice(0, selectionStart);
                const after = value.slice(selectionEnd);
                const newCursor = selectionStart + openTag.length;
                textBox.value = before + openTag + closeTag + after;
                textBox.selectionStart = textBox.selectionEnd = newCursor;
            } else {
                // Replace selected text with tags around it
                const before = value.slice(0, selectionStart);
                const selected = value.slice(selectionStart, selectionEnd);
                const after = value.slice(selectionEnd);
                textBox.value = before + openTag + selected + closeTag + after;
                // Keep selection around the newly wrapped text
                textBox.selectionStart = selectionStart + openTag.length;
                textBox.selectionEnd = selectionEnd + openTag.length;
            }
            return;
        }
        // All other tags: ctrl+key
        if (ev.ctrlKey && !ev.altKey && bbCodeCombinations.has(key) && key !== "c") {
            ev.preventDefault();
            const textBox = ev.target;
            const [openTag, closeTag] = bbCodeCombinations.get(key);
            const { selectionStart, selectionEnd, value } = textBox;
            if (selectionStart === selectionEnd) {
                // No selection: insert empty tags and place cursor between them
                const before = value.slice(0, selectionStart);
                const after = value.slice(selectionEnd);
                const newCursor = selectionStart + openTag.length;
                textBox.value = before + openTag + closeTag + after;
                textBox.selectionStart = textBox.selectionEnd = newCursor;
            } else {
                // Replace selected text with tags around it
                const before = value.slice(0, selectionStart);
                const selected = value.slice(selectionStart, selectionEnd);
                const after = value.slice(selectionEnd);
                textBox.value = before + openTag + selected + closeTag + after;
                // Keep selection around the newly wrapped text
                textBox.selectionStart = selectionStart + openTag.length;
                textBox.selectionEnd = selectionEnd + openTag.length;
            }
            return;
        }
    }
    document.getElementById("qrbody")?.addEventListener("keydown", replyKeyboardShortcuts);

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // Custom CSS injection
    function addCustomCSS(css) {
        if (!css) return;
        const style = document.createElement('style');
        style.type = 'text/css';
        style.appendChild(document.createTextNode(css));
        document.head.appendChild(style);
    }
    // Get the current URL path
    const currentPath = window.location.pathname.toLowerCase();
    const currentHost = window.location.hostname.toLowerCase();

    // Apply CSS based on URL pattern
    // Thread page CSS
    if (/\/res\/[^/]+\.html$/.test(currentPath)) {
        const css = `
/* Quick Reply */
:root.sticky-qr #quick-reply {
display: block;
top: auto !important;
bottom: 0;
left: auto !important;
position: fixed;
right: 0 !important;
}
:root.bottom-header #quick-reply {
bottom: 28px !important;
}
#quick-reply {
padding: 0;
opacity: 0.7;
transition: opacity 0.3s ease;
}
#quick-reply:hover,
#quick-reply:focus-within {
opacity: 1;
}
#qrbody {
resize: vertical;
max-height: 50vh;
height: 130px;
}
.floatingMenu {
padding: 0 !important;
}
#qrFilesBody {
max-width: 300px;
}
/* Banner */
:root.disable-banner #bannerImage {
display: none;
}
:root.ss-sidebar #bannerImage {
width: 305px;
right: 0;
position: fixed;
top: 26px;
}
:root.ss-sidebar.bottom-header #bannerImage {
top: 0 !important;
}
.innerUtility.top {
margin-top: 2em;
background-color: transparent !important;
color: var(--link-color) !important;
}
.innerUtility.top a {
color: var(--link-color) !important;
}
.quoteTooltip {
z-index: 110;
}
/* (You) Replies */
.innerPost:has(.youName) {
border-left: dashed #68b723 3px;
}
.innerPost:has(.quoteLink.you) {
border-left: solid #dd003e 3px;
}
/* Filename & Thumbs */
.originalNameLink {
display: inline;
overflow-wrap: anywhere;
white-space: normal;
}
.multipleUploads .uploadCell:not(.expandedCell) {
  max-width: 215px;
}
`;
        addCustomCSS(css);
    }

    if (/^8chan\.(se|moe)$/.test(currentHost)) {
        // General CSS for all pages
        const css = `
/* Margins */
body {
margin: 0;
}
:root.ss-sidebar #mainPanel {
margin-right: 305px;
}
/* Cleanup */
#navFadeEnd,
#navFadeMid,
#navTopBoardsSpan,
.coloredIcon.linkOverboard,
.coloredIcon.linkSfwOver,
.coloredIcon.multiboardButton,
#navLinkSpan>span:nth-child(9),
#navLinkSpan>span:nth-child(11),
#navLinkSpan>span:nth-child(13) {
display: none;
}
footer {
visibility: hidden;
height: 0;
}
/* Header */
:not(:root.bottom-header) .navHeader {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
:root.bottom-header nav.navHeader {
top: auto !important;
bottom: 0 !important;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.15);
}
/* Thread Watcher */
#watchedMenu {
font-size: smaller;
padding: 5px !important;
box-shadow: -3px 3px 2px 0px rgba(0,0,0,0.19);
}
#watchedMenu,
#watchedMenu .floatingContainer {
min-width: 200px;
}
#watchedMenu .watchedCellLabel > a:after {
content: " - "attr(href);
filter: saturate(50%);
font-style: italic;
font-weight: bold;
}
td.watchedCell > label.watchedCellLabel {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 180px;
display: block;
}
td.watchedCell > label.watchedCellLabel:hover {
overflow: unset;
width: auto;
white-space: normal;
}
/* Posts */
.quoteTooltip .innerPost {
overflow: hidden;
box-shadow: -3px 3px 2px 0px rgba(0,0,0,0.19);
}
:root.fit-replies .innerPost {
margin-left: 10px;
display: flow-root;
}
.scroll-arrow-btn {
position: fixed;
right: 50px;
width: 36px;
height: 35px;
background: #222;
color: #fff;
border: none;
border-radius: 50%;
box-shadow: 0 2px 8px rgba(0,0,0,0.18);
font-size: 22px;
cursor: pointer;
opacity: 0.7;
z-index: 99998;
display: flex;
align-items: center;
justify-content: center;
transition: opacity 0.2s, background 0.2s;
}
:root.ss-sidebar .scroll-arrow-btn {
right: 330px !important;
}
.scroll-arrow-btn:hover {
opacity: 1;
background: #444;
}
#scroll-arrow-up {
bottom: 80px;
}
#scroll-arrow-down {
bottom: 32px;
}
`;
        addCustomCSS(css);
    }

    // Catalog page CSS
    if (/\/catalog\.html$/.test(currentPath)) {
        const css = `
#dynamicAnnouncement {
display: none;
}
#postingForm {
margin: 2em auto;
}
`;
        addCustomCSS(css);
    }
})();