Discord Message Colorizer Enhanced

Colors <em> text yellow and text within parentheses blue in Discord messages, handling split spans

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Discord Message Colorizer Enhanced
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Colors <em> text yellow and text within parentheses blue in Discord messages, handling split spans
// @author       Vishanka
// @match        https://discord.com/channels/*
// @grant        none
// ==/UserScript==



(function() {
    // 1. Inject CSS classes for styling
    function injectStyles() {
        const style = document.createElement('style');
        style.textContent = `
            /* Baseline color for all message content */
            div[class*="messageContent_"] {
                color: #A2A2AC;
            }

            /* Highlight colors for specific patterns */
            .highlight-yellow {
                color: #E0DF7F !important;
            }
            .highlight-blue {
                color: #737373 !important;
            }
            .highlight-white {
                color: #FFFFFF !important;
            }
        `;
        document.head.appendChild(style);
    }

    // 2. Function to style message content
    function styleMessageContent() {
        // Select all message items based on the class prefix
        const messageItems = document.querySelectorAll('li[class^="messageListItem_"]');

        messageItems.forEach(messageItem => {
            const contentDiv = messageItem.querySelector('div[class*="messageContent_"]');
            if (!contentDiv) return; // Skip if no content div found

            // a. Color all <em> elements yellow
            const emElements = contentDiv.querySelectorAll('em');
            emElements.forEach(em => {
                if (!em.classList.contains('highlight-yellow')) { // Prevent reapplying
                    em.classList.add('highlight-yellow');
                }
            });

            // b. Color text within parentheses blue
            colorTextWithinDelimiters(contentDiv, '(', ')', 'highlight-blue', false);

            // c. Color text within quotation marks white (#FFFFFF)
            // Supports straight quotes and smart quotes
            colorTextWithinQuotes(contentDiv, ['"', '“', '‘'], ['"', '”', '’'], 'highlight-white');
        });
    }

    /**
     * Helper function to color text within matched quotation marks.
     * @param {HTMLElement} container - The container element to search within.
     * @param {string[]} openDelimiters - The opening quotation marks.
     * @param {string[]} closeDelimiters - The closing quotation marks.
     * @param {string} highlightClass - The CSS class to apply for highlighting.
     */
    function colorTextWithinQuotes(container, openDelimiters, closeDelimiters, highlightClass) {
        const spans = Array.from(container.querySelectorAll('span'));
        let buffer = []; // Collect spans inside the current quote

        spans.forEach(span => {
            const text = span.textContent;

            // Process span content character by character
            for (let i = 0; i < text.length; i++) {
                const char = text[i];

                if (openDelimiters.includes(char) && buffer.length === 0) {
                    // Start a new quote
                    buffer.push(span);
                } else if (closeDelimiters.includes(char) && buffer.length > 0) {
                    // End the current quote
                    buffer.forEach(s => s.classList.add(highlightClass));
                    buffer = []; // Clear buffer
                } else if (buffer.length > 0) {
                    // Inside a quote
                    buffer.push(span);
                }
            }
        });
    }

    /**
     * Helper function to color text within specified delimiters.
     * For non-quotation mark delimiters like parentheses.
     * @param {HTMLElement} container - The container element to search within.
     * @param {string|string[]} openDelimiter - The opening delimiter character(s).
     * @param {string|string[]} closeDelimiter - The closing delimiter character(s).
     * @param {string} highlightClass - The CSS class to apply for highlighting.
     * @param {boolean} isToggle - Whether to toggle highlighting (true for quotes).
     */
    function colorTextWithinDelimiters(container, openDelimiter, closeDelimiter, highlightClass, isToggle) {
        const spans = container.querySelectorAll('span');
        let isWithin = false;

        // Normalize delimiters to arrays
        const openDelims = Array.isArray(openDelimiter) ? openDelimiter : [openDelimiter];
        const closeDelims = Array.isArray(closeDelimiter) ? closeDelimiter : [closeDelimiter];

        spans.forEach(span => {
            const text = span.textContent;

            if (isToggle) {
                let hasOpening = false;
                let hasClosing = false;

                // Check for any closing delimiters first
                closeDelims.forEach(close => {
                    if (text.includes(close)) {
                        hasClosing = true;
                    }
                });

                if (hasClosing && isWithin) {
                    // Apply highlight before closing
                    span.classList.add(highlightClass);
                    isWithin = false;
                }

                // Apply highlight if currently within delimiters
                if (isWithin) {
                    span.classList.add(highlightClass);
                }

                // Check for any opening delimiters
                openDelims.forEach(open => {
                    if (text.includes(open)) {
                        hasOpening = true;
                        isWithin = true;
                    }
                });

                if (hasOpening) {
                    // Apply highlight for the span containing the opening delimiter
                    span.classList.add(highlightClass);
                }
            } else {
                // Non-toggle: e.g., parentheses
                if (text.includes(openDelimiter)) {
                    isWithin = true;
                }

                if (isWithin) {
                    span.classList.add(highlightClass);
                }

                if (text.includes(closeDelimiter)) {
                    isWithin = false;
                }
            }
        });
    }

    // 3. Initialize the styling process
    function initializeStyling() {
        injectStyles();
        styleMessageContent();

        // Observe for new messages being added to the DOM
        const observer = new MutationObserver(mutations => {
            mutations.forEach(() => {
                styleMessageContent();
            });
        });

        // Start observing the entire document body for changes
        observer.observe(document.body, { childList: true, subtree: true });
    }

    // 4. Run the initialization
    initializeStyling();
})();