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.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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();
    }
})();