您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a Light/Dark mode toggle at the bottom left
// ==UserScript== // @name Grok Theme Toggle // @namespace nisc // @version 2025.06.08-A // @description Adds a Light/Dark mode toggle at the bottom left // @homepageURL https://github.com/nisc/grok-userscripts/ // @author nisc // @match https://grok.com/* // @icon https://grok.com/images/favicon-light.png // @run-at document-end // @grant none // ==/UserScript== (function() { 'use strict'; const CONFIG = { THEME: { KEY: 'forcedTheme', MODES: { DARK: 'dark', LIGHT: 'light' }, DEFAULT: 'dark', COLORS: { dark: { bg: '#333', text: '#fff', border: '#333' }, light: { bg: '#fff', text: '#000', border: '#333' } }, ICONS: { dark: '🌕', light: '🌑' } }, UI: { POSITION_MARGIN: 20, BUTTON_STYLES: { position: 'fixed', zIndex: '10000', padding: '8px 12px', border: '0.25px solid #333', borderRadius: '8px', cursor: 'pointer', fontSize: '14px', boxShadow: '0 2px 5px rgba(0,0,0,0.2)' }, LIGHT_MODE_STYLES: ` .light .text-xs.font-semibold { color: #000 !important; } ` } }; // Track current theme state let currentTheme = localStorage.getItem(CONFIG.THEME.KEY) || CONFIG.THEME.DEFAULT; /** * Applies theme to document root only if it differs from current state * Handles both class names and color-scheme property */ function applyTheme(theme) { const htmlElement = document.documentElement; const isDark = htmlElement.classList.contains(CONFIG.THEME.MODES.DARK); const isLight = htmlElement.classList.contains(CONFIG.THEME.MODES.LIGHT); if (theme === CONFIG.THEME.MODES.DARK && !isDark) { htmlElement.classList.add(CONFIG.THEME.MODES.DARK); htmlElement.classList.remove(CONFIG.THEME.MODES.LIGHT); htmlElement.style.setProperty('color-scheme', CONFIG.THEME.MODES.DARK, 'important'); } else if (theme === CONFIG.THEME.MODES.LIGHT && !isLight) { htmlElement.classList.add(CONFIG.THEME.MODES.LIGHT); htmlElement.classList.remove(CONFIG.THEME.MODES.DARK); htmlElement.style.setProperty('color-scheme', CONFIG.THEME.MODES.LIGHT, 'important'); } } // Create and inject custom style element const styleElement = document.createElement('style'); document.head.appendChild(styleElement); /** * Updates custom styles based on current theme * Primarily handles special cases for light mode */ function updateStyles(theme) { styleElement.textContent = theme === CONFIG.THEME.MODES.LIGHT ? CONFIG.UI.LIGHT_MODE_STYLES : ''; } /** * Toggles between light and dark themes * Updates localStorage, applies theme, and updates UI */ function toggleTheme() { currentTheme = currentTheme === CONFIG.THEME.MODES.DARK ? CONFIG.THEME.MODES.LIGHT : CONFIG.THEME.MODES.DARK; localStorage.setItem(CONFIG.THEME.KEY, currentTheme); applyTheme(currentTheme); updateStyles(currentTheme); updateButtonAppearance(); } /** * Updates toggle button appearance based on current theme * Changes icon, background, text, and border colors */ function updateButtonAppearance() { const themeColors = CONFIG.THEME.COLORS[currentTheme]; toggleButton.textContent = CONFIG.THEME.ICONS[currentTheme]; toggleButton.style.backgroundColor = themeColors.bg; toggleButton.style.color = themeColors.text; toggleButton.style.borderColor = themeColors.border; } // Create and configure theme toggle button const toggleButton = document.createElement('button'); Object.assign(toggleButton.style, CONFIG.UI.BUTTON_STYLES); updateButtonAppearance(); toggleButton.addEventListener('click', toggleTheme); document.body.appendChild(toggleButton); // Position button at bottom left const buttonHeight = toggleButton.offsetHeight; const initialLeft = CONFIG.UI.POSITION_MARGIN; const initialTop = Math.max(0, window.innerHeight - buttonHeight - CONFIG.UI.POSITION_MARGIN); toggleButton.style.left = `${initialLeft}px`; toggleButton.style.top = `${initialTop}px`; // Always maintain bottom left position on window resize window.addEventListener('resize', () => { const buttonHeight = toggleButton.offsetHeight; const newTop = Math.max(0, window.innerHeight - buttonHeight - CONFIG.UI.POSITION_MARGIN); toggleButton.style.top = `${newTop}px`; }); // Initialize theme applyTheme(currentTheme); updateStyles(currentTheme); // Watch for external theme changes and maintain chosen theme const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.type === 'attributes' && (mutation.attributeName === 'class' || mutation.attributeName === 'style')) { applyTheme(currentTheme); } }); }); observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class', 'style'] }); })();