X.com Custom Verification Badges

Customize verification checkmark colors by clicking on them

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         X.com Custom Verification Badges
// @namespace    http://violentmonkey.github.io
// @version      1.0
// @description  Customize verification checkmark colors by clicking on them
// @author       You
// @match        https://x.com/*
// @match        https://twitter.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @run-at       document-start
// @license      MIT
// ==/UserScript==


(function() {
    'use strict';

    const STORAGE_KEY = 'x_custom_checkmark_colors';
    const DEFAULT_COLORS = {
        blue: '#1d9bf0',      // Original blue
        gold: '#ffd700',      // Premium gold
        pink: '#ff69b4',      // Pink
        purple: '#8b5cf6',    // Purple
        green: '#10b981',     // Green
        red: '#ef4444',       // Red
        orange: '#f97316',    // Orange
        cyan: '#06b6d4'       // Cyan
    };

    let userColors = GM_getValue(STORAGE_KEY, {
        verified: DEFAULT_COLORS.blue,
        premium: DEFAULT_COLORS.gold
    });

    let isColorPickerOpen = false;

    // Apply custom colors immediately
    applyCheckmarkColors();

    function applyCheckmarkColors() {
        const styleId = 'x-custom-checkmark-styles';
        let styleElement = document.getElementById(styleId);

        if (!styleElement) {
            styleElement = document.createElement('style');
            styleElement.id = styleId;
            document.head.appendChild(styleElement);
        }

        const css = `
            /* Verified accounts (blue check) */
            [data-testid="icon-verified"],
            svg[aria-label="Verified account"],
            .r-1cvl2hr[data-testid="icon-verified"],
            [aria-label="Verified account"] {
                color: ${userColors.verified} !important;
                fill: ${userColors.verified} !important;
            }

            /* Premium accounts (gold check) */
            [data-testid="premiumIcon"],
            [aria-label="Premium account"],
            .r-1cvl2hr[data-testid="premiumIcon"] {
                color: ${userColors.premium} !important;
                fill: ${userColors.premium} !important;
            }

            /* Hover effects for clickable checkmarks */
            .custom-checkmark-clickable:hover {
                opacity: 0.7 !important;
                transform: scale(1.1) !important;
                cursor: pointer !important;
                transition: all 0.2s ease !important;
            }

            /* Color picker styles */
            #checkmark-color-picker {
                position: fixed !important;
                top: 50% !important;
                left: 50% !important;
                transform: translate(-50%, -50%) !important;
                background: #15202b !important;
                border: 1px solid #38444d !important;
                border-radius: 16px !important;
                padding: 20px !important;
                z-index: 10000 !important;
                box-shadow: 0 4px 20px rgba(0,0,0,0.3) !important;
                min-width: 300px !important;
                font-family: system-ui, -apple-system, sans-serif !important;
            }

            #checkmark-color-picker-overlay {
                position: fixed !important;
                top: 0 !important;
                left: 0 !important;
                width: 100% !important;
                height: 100% !important;
                background: rgba(0,0,0,0.5) !important;
                z-index: 9999 !important;
            }

            .color-option {
                width: 40px !important;
                height: 40px !important;
                border-radius: 8px !important;
                margin: 5px !important;
                cursor: pointer !important;
                border: 2px solid transparent !important;
                transition: all 0.2s ease !important;
            }

            .color-option:hover {
                transform: scale(1.1) !important;
                border-color: white !important;
            }

            .color-option.selected {
                border-color: white !important;
                box-shadow: 0 0 0 2px #1d9bf0 !important;
            }
        `;

        styleElement.textContent = css;
    }

    function makeCheckmarksClickable() {
        // Find all verification badges
        const checkmarks = document.querySelectorAll(`
            [data-testid="icon-verified"],
            [data-testid="premiumIcon"],
            svg[aria-label="Verified account"],
            [aria-label="Verified account"],
            [aria-label="Premium account"],
            .r-1cvl2hr[data-testid="icon-verified"],
            .r-1cvl2hr[data-testid="premiumIcon"]
        `);

        checkmarks.forEach(checkmark => {
            if (!checkmark.classList.contains('custom-checkmark-clickable')) {
                checkmark.classList.add('custom-checkmark-clickable');

                // Wrap in a clickable container if needed
                let clickableElement = checkmark;
                if (checkmark.tagName === 'svg' || checkmark.tagName === 'path') {
                    clickableElement = checkmark.closest('div') || checkmark;
                }

                clickableElement.style.cursor = 'pointer';
                clickableElement.title = 'Click to change color';

                clickableElement.addEventListener('click', (e) => {
                    e.preventDefault();
                    e.stopPropagation();

                    const isPremium = checkmark.getAttribute('data-testid') === 'premiumIcon' ||
                                    checkmark.getAttribute('aria-label') === 'Premium account';

                    showColorPicker(isPremium ? 'premium' : 'verified');
                });
            }
        });
    }

    function showColorPicker(checkmarkType) {
        if (isColorPickerOpen) return;
        isColorPickerOpen = true;

        closeColorPicker(); // Remove any existing picker

        const overlay = document.createElement('div');
        overlay.id = 'checkmark-color-picker-overlay';

        const picker = document.createElement('div');
        picker.id = 'checkmark-color-picker';

        const currentColor = userColors[checkmarkType];
        const checkmarkName = checkmarkType === 'verified' ? 'Verified' : 'Premium';

        picker.innerHTML = `
            <h3 style="color: white; margin: 0 0 16px 0; text-align: center;">
                Change ${checkmarkName} Checkmark Color
            </h3>
            <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-bottom: 20px;">
                ${Object.entries(DEFAULT_COLORS).map(([name, color]) => `
                    <div class="color-option ${color === currentColor ? 'selected' : ''}"
                         data-color="${color}"
                         style="background: ${color};"
                         title="${name.charAt(0).toUpperCase() + name.slice(1)}">
                    </div>
                `).join('')}
            </div>
            <div style="display: flex; gap: 10px; justify-content: center;">
                <button id="custom-color-btn" style="
                    padding: 8px 16px;
                    background: #1d9bf0;
                    color: white;
                    border: none;
                    border-radius: 20px;
                    cursor: pointer;
                    font-weight: bold;
                ">Custom Color</button>
                <button id="reset-color-btn" style="
                    padding: 8px 16px;
                    background: transparent;
                    color: #1d9bf0;
                    border: 1px solid #1d9bf0;
                    border-radius: 20px;
                    cursor: pointer;
                    font-weight: bold;
                ">Reset</button>
                <button id="close-picker-btn" style="
                    padding: 8px 16px;
                    background: transparent;
                    color: #64748b;
                    border: 1px solid #64748b;
                    border-radius: 20px;
                    cursor: pointer;
                    font-weight: bold;
                ">Close</button>
            </div>
            <div id="custom-color-input" style="margin-top: 16px; display: none;">
                <input type="color" id="color-picker-input" value="${currentColor}" style="width: 100%; height: 40px;">
                <button id="apply-custom-color" style="
                    margin-top: 8px;
                    padding: 8px 16px;
                    background: #10b981;
                    color: white;
                    border: none;
                    border-radius: 20px;
                    cursor: pointer;
                    font-weight: bold;
                    width: 100%;
                ">Apply Custom Color</button>
            </div>
        `;

        // Add event listeners
        overlay.addEventListener('click', closeColorPicker);

        picker.querySelectorAll('.color-option').forEach(option => {
            option.addEventListener('click', (e) => {
                e.stopPropagation();
                const color = option.getAttribute('data-color');
                updateCheckmarkColor(checkmarkType, color);
                closeColorPicker();
            });
        });

        picker.querySelector('#custom-color-btn').addEventListener('click', (e) => {
            e.stopPropagation();
            const customInput = picker.querySelector('#custom-color-input');
            customInput.style.display = customInput.style.display === 'none' ? 'block' : 'none';
        });

        picker.querySelector('#apply-custom-color').addEventListener('click', (e) => {
            e.stopPropagation();
            const colorInput = picker.querySelector('#color-picker-input');
            updateCheckmarkColor(checkmarkType, colorInput.value);
            closeColorPicker();
        });

        picker.querySelector('#reset-color-btn').addEventListener('click', (e) => {
            e.stopPropagation();
            const defaultColor = checkmarkType === 'verified' ? DEFAULT_COLORS.blue : DEFAULT_COLORS.gold;
            updateCheckmarkColor(checkmarkType, defaultColor);
            closeColorPicker();
        });

        picker.querySelector('#close-picker-btn').addEventListener('click', closeColorPicker);

        // Prevent picker click from closing overlay
        picker.addEventListener('click', (e) => {
            e.stopPropagation();
        });

        document.body.appendChild(overlay);
        document.body.appendChild(picker);
    }

    function closeColorPicker() {
        const overlay = document.getElementById('checkmark-color-picker-overlay');
        const picker = document.getElementById('checkmark-color-picker');

        if (overlay) overlay.remove();
        if (picker) picker.remove();

        isColorPickerOpen = false;
    }

    function updateCheckmarkColor(checkmarkType, color) {
        userColors[checkmarkType] = color;
        GM_setValue(STORAGE_KEY, userColors);
        applyCheckmarkColors();
        showNotification(`${checkmarkType === 'verified' ? 'Verified' : 'Premium'} checkmark color updated!`);
    }

    function showNotification(message) {
        // Remove existing notification
        const existingNotification = document.getElementById('checkmark-color-notification');
        if (existingNotification) existingNotification.remove();

        const notification = document.createElement('div');
        notification.id = 'checkmark-color-notification';
        notification.textContent = message;
        notification.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: #10b981;
            color: white;
            padding: 12px 20px;
            border-radius: 8px;
            font-weight: bold;
            z-index: 10001;
            animation: slideIn 0.3s ease;
            font-family: system-ui, -apple-system, sans-serif;
        `;

        // Add animation styles if not already present
        if (!document.getElementById('checkmark-animation-styles')) {
            const style = document.createElement('style');
            style.id = 'checkmark-animation-styles';
            style.textContent = `
                @keyframes slideIn {
                    from { transform: translateX(100%); opacity: 0; }
                    to { transform: translateX(0); opacity: 1; }
                }
            `;
            document.head.appendChild(style);
        }

        document.body.appendChild(notification);

        setTimeout(() => {
            if (notification.parentNode) {
                notification.parentNode.removeChild(notification);
            }
        }, 3000);
    }

    // Initialize and watch for new checkmarks
    function init() {
        makeCheckmarksClickable();

        // Set up observer for dynamically loaded content
        const observer = new MutationObserver((mutations) => {
            let shouldCheck = false;

            for (const mutation of mutations) {
                if (mutation.type === 'childList') {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === 1) {
                            if (node.querySelector?.('[data-testid="icon-verified"], [data-testid="premiumIcon"]')) {
                                shouldCheck = true;
                                break;
                            }
                        }
                    }
                }
                if (shouldCheck) break;
            }

            if (shouldCheck) {
                setTimeout(makeCheckmarksClickable, 100);
            }
        });

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

        // Also check periodically
        setInterval(makeCheckmarksClickable, 2000);
    }

    // Start the script
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        setTimeout(init, 1000);
    }

})();