Grok.com Theme Switcher

Adds a theme switcher to grok.com with dark, cyberpunk, light, blood red, midnight, deep ocean, celestial, and divine modes, foldable picker with animations, draggable, and script editor compatibility. Enhanced query-bar opacity fix to 75% with broader selectors and debug logging.

// ==UserScript==
// @name         Grok.com Theme Switcher
// @namespace    http://violentmonkey.github.io/
// @version      1.2
// @description  Adds a theme switcher to grok.com with dark, cyberpunk, light, blood red, midnight, deep ocean, celestial, and divine modes, foldable picker with animations, draggable, and script editor compatibility. Enhanced query-bar opacity fix to 75% with broader selectors and debug logging.
// @author       virtualdmns
// @license      MIT
// @match        https://grok.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    console.log('Grok Theme Switcher: Script loaded.');

    // Define theme styles
    const themes = {
        dark: {
            background: '#1a1a1a',
            text: '#e0e0e0',
            accent: '#00ff88',
            buttonBg: '#333',
            buttonText: '#fff'
        },
        cyberpunk: {
            background: '#0d0221',
            text: '#ff00ff',
            accent: '#00f7ff',
            buttonBg: '#2a0a3b',
            buttonText: '#ff00ff'
        },
        light: {
            background: '#f5f5f5',
            text: '#333',
            accent: '#007bff',
            buttonBg: '#ddd',
            buttonText: '#000'
        },
        bloodred: {
            background: '#2e0000',
            text: '#ff6666',
            accent: '#800000',
            buttonBg: '#4a0000',
            buttonText: '#ff9999'
        },
        midnight: {
            background: '#0a0a1c',
            text: '#c0c0d9',
            accent: '#4b4bff',
            buttonBg: '#1c1c2e',
            buttonText: '#e6e6ff'
        },
        deepocean: {
            background: '#0a2424',
            text: '#66cccc',
            accent: '#00b7eb',
            buttonBg: '#1a3c3c',
            buttonText: '#99e6e6'
        },
        celestial: {
            background: '#1b0a3b',
            text: '#ffd700',
            accent: '#b266ff',
            buttonBg: '#2e1a5c',
            buttonText: '#ffeb99'
        },
        divine: {
            background: '#f5f6f5',
            text: '#4682b4',
            accent: '#c0c0c0',
            buttonBg: '#e6e6fa',
            buttonText: '#191970'
        }
    };

    // Debug function to check query-bar opacity
    function debugQueryBarOpacity() {
        const queryBar = document.querySelector('div[class*="query-bar"]');
        if (queryBar) {
            const computedStyle = window.getComputedStyle(queryBar);
            console.log(`Grok Theme Switcher: Query bar opacity is ${computedStyle.opacity}`);
        } else {
            console.log('Grok Theme Switcher: Query bar not found in DOM.');
        }
    }

    // Inject CSS to avoid CSP issues
    function injectStyles(themeName) {
        console.log(`Grok Theme Switcher: Injecting styles for theme - ${themeName}`);
        const theme = themes[themeName];
        let styleTag = document.getElementById('theme-styles');
        if (!styleTag) {
            styleTag = document.createElement('style');
            styleTag.id = 'theme-styles';
            document.head.appendChild(styleTag);
        }

        styleTag.textContent = `
            @keyframes fade-slide {
                from { opacity: 0; transform: translateY(10px); }
                to { opacity: 1; transform: translateY(0); }
            }
            @keyframes scale-pop {
                from { transform: scale(0.5); opacity: 0; }
                to { transform: scale(1); opacity: 1; }
            }
            @keyframes pulse {
                0% { transform: scale(1); }
                50% { transform: scale(1.1); }
                100% { transform: scale(1); }
            }
            body {
                background-color: ${theme.background} !important;
                color: ${theme.text} !important;
                animation: fade-slide 0.5s ease !important;
            }
            p, span, div:not([class*="editor"]), h1, h2, h3, h4, h5, h6, li, a:not([class*="button"]), img {
                color: ${theme.text} !important;
                background-color: transparent !important;
                border: none !important;
                animation: fade-slide 0.5s ease !important;
            }
            button:not([class*="editor"]), input[type="button"], input[type="submit"], [role="button"]:not([class*="editor"]) {
                background-color: ${theme.buttonBg} !important;
                color: ${theme.buttonText} !important;
                border: none !important;
                transition: all 0.3s ease !important;
                animation: fade-slide 0.5s ease !important;
            }
            div[class*="query-bar"] {
                opacity: 0.75 !important;
                transition: opacity 0.3s ease !important;
                background-color: ${theme.background} !important;
            }
            div[class*="query-bar"] textarea, div[class*="query-bar"] button, div[class*="query-bar"] div {
                background-color: ${theme.background} !important;
                opacity: 1 !important;
            }
            #theme-switcher {
                position: fixed !important;
                z-index: 9999 !important;
                background-color: rgba(0, 0, 0, 0.7) !important;
                padding: 10px !important;
                border-radius: 8px !important;
                cursor: move !important;
                animation: scale-pop 0.3s ease !important;
                display: block !important;
            }
            #theme-switcher.hidden {
                display: none !important;
            }
            #themeSelect {
                padding: 8px !important;
                border-radius: 4px !important;
                cursor: pointer !important;
                background: #fff !important;
                color: #000 !important;
            }
            #theme-toggle-btn {
                position: fixed !important;
                z-index: 9999 !important;
                background-color: rgba(0, 0, 0, 0.7) !important;
                color: #fff !important;
                padding: 8px 12px !important;
                border-radius: 4px !important;
                cursor: pointer !important;
                font-size: 14px !important;
                animation: pulse 2s infinite ease !important;
                display: none !important;
            }
            #theme-toggle-btn.visible {
                display: block !important;
            }
        `;
        console.log('Grok Theme Switcher: Applied opacity 0.75 to query-bar');
        setTimeout(debugQueryBarOpacity, 500); // Check opacity after styles apply
    }

    // Force toggle state
    function forceToggleState(switcher, toggleBtn, isFolded) {
        console.log(`Grok Theme Switcher: Forcing toggle state - folded: ${isFolded}`);
        switcher.classList.toggle('hidden', isFolded);
        toggleBtn.classList.toggle('visible', isFolded);
        localStorage.setItem('themePickerFolded', isFolded.toString());
    }

    // Create floating theme switcher UI
    function createSwitcher() {
        console.log('Grok Theme Switcher: Creating switcher UI.');
        if (document.getElementById('theme-switcher') || document.getElementById('theme-toggle-btn')) {
            console.log('Grok Theme Switcher: Switcher or toggle already exists, skipping.');
            return;
        }

        // Create toggle button
        const toggleBtn = document.createElement('button');
        toggleBtn.id = 'theme-toggle-btn';
        toggleBtn.textContent = 'Theme';
        document.body.appendChild(toggleBtn);

        // Create theme switcher
        const switcher = document.createElement('div');
        switcher.id = 'theme-switcher';
        switcher.innerHTML = `
            <select id="themeSelect">
                <option value="dark">Dark</option>
                <option value="cyberpunk">Cyberpunk</option>
                <option value="light">Light</option>
                <option value="bloodred">Blood Red</option>
                <option value="midnight">Midnight</option>
                <option value="deepocean">Deep Ocean</option>
                <option value="celestial">Celestial</option>
                <option value="divine">Divine</option>
            </select>
        `;

        // Load saved position or default
        let savedPos = JSON.parse(localStorage.getItem('themePickerPos'));
        if (!savedPos || !savedPos.left || !savedPos.top) {
            savedPos = { top: 'auto', bottom: '20px', left: 'auto', right: '20px' };
        }
        Object.assign(switcher.style, savedPos);
        Object.assign(toggleBtn.style, savedPos);

        // Load folded state
        const isFolded = localStorage.getItem('themePickerFolded') === 'true';
        document.body.appendChild(switcher);
        forceToggleState(switcher, toggleBtn, isFolded);

        // Event listener for theme change
        const themeSelect = document.getElementById('themeSelect');
        themeSelect.addEventListener('change', (e) => {
            console.log(`Grok Theme Switcher: Theme selected - ${e.target.value}`);
            injectStyles(e.target.value);
            localStorage.setItem('grokTheme', e.target.value);
        });

        // Event listener for toggle button
        toggleBtn.addEventListener('click', () => {
            console.log('Grok Theme Switcher: Toggle clicked.');
            const isCurrentlyFolded = switcher.classList.contains('hidden');
            forceToggleState(switcher, toggleBtn, !isCurrentlyFolded);
        });

        // Draggable functionality for both switcher and button
        function makeDraggable(element, isButton) {
            let isDragging = false;
            let offsetX, offsetY;

            element.addEventListener('mousedown', (e) => {
                isDragging = true;
                offsetX = e.offsetX;
                offsetY = e.offsetY;
                console.log(`Grok Theme Switcher: Starting drag on ${isButton ? 'button' : 'switcher'}.`);
            });

            document.addEventListener('mousemove', (e) => {
                if (isDragging) {
                    let newLeft = e.clientX - offsetX;
                    let newTop = e.clientY - offsetY;

                    // Keep within viewport
                    const rect = element.getBoundingClientRect();
                    newLeft = Math.max(0, Math.min(newLeft, window.innerWidth - rect.width));
                    newTop = Math.max(0, Math.min(newTop, window.innerHeight - rect.height));

                    element.style.left = `${newLeft}px`;
                    element.style.top = `${newTop}px`;
                    element.style.right = 'auto';
                    element.style.bottom = 'auto';

                    // Sync other element
                    const otherElement = isButton ? switcher : toggleBtn;
                    otherElement.style.left = `${newLeft}px`;
                    otherElement.style.top = `${newTop}px`;
                    otherElement.style.right = 'auto';
                    otherElement.style.bottom = 'auto';
                }
            });

            document.addEventListener('mouseup', () => {
                if (isDragging) {
                    isDragging = false;
                    const pos = {
                        top: element.style.top,
                        bottom: element.style.bottom,
                        left: element.style.left,
                        right: element.style.right
                    };
                    localStorage.setItem('themePickerPos', JSON.stringify(pos));
                    console.log(`Grok Theme Switcher: Position saved.`, pos);
                }
            });
        }

        makeDraggable(switcher, false);
        makeDraggable(toggleBtn, true);

        const savedTheme = localStorage.getItem('grokTheme') || 'bloodred';
        themeSelect.value = savedTheme;
        injectStyles(savedTheme);
    }

    // Mutation observer for dynamic content
    function observeDOM() {
        console.log('Grok Theme Switcher: Setting up DOM observer.');
        const observer = new MutationObserver(() => {
            console.log('Grok Theme Switcher: DOM changed, reapplying theme.');
            const savedTheme = localStorage.getItem('grokTheme') || 'bloodred';
            injectStyles(savedTheme);
            debugQueryBarOpacity();
            const switcher = document.getElementById('theme-switcher');
            const toggleBtn = document.getElementById('theme-toggle-btn');
            if (switcher && toggleBtn) {
                const isFolded = localStorage.getItem('themePickerFolded') === 'true';
                forceToggleState(switcher, toggleBtn, isFolded);
            } else if (!switcher && !toggleBtn) {
                console.log('Grok Theme Switcher: Switcher missing, recreating.');
                createSwitcher();
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['class', 'style']
        });
    }

    // Wait for DOM to be ready
    function waitForBody() {
        if (document.body) {
            initialize();
        } else {
            console.log('Grok Theme Switcher: Body not ready, polling...');
            setTimeout(waitForBody, 100);
        }
    }

    // Start when DOM is ready or poll if not
    function initialize() {
        console.log('Grok Theme Switcher: Initializing.');
        createSwitcher();
        observeDOM();
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', waitForBody);
    } else {
        waitForBody();
    }
})();