您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A smarter dark mode script that prevents conflicts with native dark themes and offers user-friendly controls (toggle with Ctrl+D).
// ==UserScript== // @name Efficient & Refined Dark Mode // @namespace http://tampermonkey.net/ // @version 20250827.1 // @description A smarter dark mode script that prevents conflicts with native dark themes and offers user-friendly controls (toggle with Ctrl+D). // @author Lancelotly (Refined by Gemini) // @match *://*/* // @run-at document-start // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @icon data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm-1.5 16.5A7.5 7.5 0 0 1 12 4.5V12a7.5 7.5 0 0 1 7.5 7.5H12a7.5 7.5 0 0 1-1.5-3z" fill="%23ffd900" /></svg> // ==/UserScript== (function() { 'use strict'; // --- Configuration & State --- const STORAGE_KEYS = { THEME_STATE: 'darkModeThemeState', // User's preference: 'auto', 'dark', 'light' DISABLED_SITES: 'darkModeDisabledSites' }; const state = { styleElement: null, preemptiveCurtain: null, isDarkApplied: false, userThemePref: GM_getValue(STORAGE_KEYS.THEME_STATE, 'auto'), disabledSites: JSON.parse(GM_getValue(STORAGE_KEYS.DISABLED_SITES, '[]')) }; // --- Core Functions --- /** * Checks if the user's OS/browser preference is set to dark mode. */ function userPrefersDark() { return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; } /** * A more robust check to determine if the website already has a dark theme. */ function isSiteAlreadyDark() { return new Promise(resolve => { const performCheck = () => { const htmlElement = document.documentElement; const bodyElement = document.body; // 1. Check for explicit color-scheme meta tag or CSS property const colorSchemeMeta = document.querySelector('meta[name="color-scheme"]'); if (colorSchemeMeta && colorSchemeMeta.content.includes('dark')) { resolve(true); return; } if (getComputedStyle(htmlElement).getPropertyValue('color-scheme').includes('dark')) { resolve(true); return; } // 2. Check for common dark theme attributes and classes const darkAttributes = ['dark', 'darker-dark-theme', 'darker-dark-theme-deprecate']; for (const attr of darkAttributes) { if (htmlElement.hasAttribute(attr)) { resolve(true); return; } } if (htmlElement.dataset.theme === 'dark' || htmlElement.dataset.bsTheme === 'dark') { resolve(true); return; } if (bodyElement.dataset.cmColorScheme === 'dark' || bodyElement.dataset.cmDarkLaunched === 'true') { resolve(true); return; } for (const className of [...htmlElement.classList, ...bodyElement.classList]) { if (className.toLowerCase().includes('dark')) { resolve(true); return; } } const darkClasses = ['dark', 'dark-mode', 'night', 'night-mode']; for (const cls of darkClasses) { if (bodyElement.classList.contains(cls) || htmlElement.classList.contains(cls)) { resolve(true); return; } } // 3. Heuristic: Check body background color luminosity const bgColor = getComputedStyle(bodyElement).backgroundColor; if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)') { const rgb = bgColor.match(/\d+/g); if (rgb) { const luminance = (0.2126 * parseInt(rgb[0]) + 0.7152 * parseInt(rgb[1]) + 0.0722 * parseInt(rgb[2])); if (luminance < 100) { resolve(true); return; } } } resolve(false); }; if (document.body) { performCheck(); } else { new MutationObserver((_, observer) => { if (document.body) { observer.disconnect(); performCheck(); } }).observe(document.documentElement, { childList: true }); } }); } /** * Applies the full, filter-based dark mode styles. */ function applyDarkStyles() { if (state.isDarkApplied) return; const darkCss = ` :root { --dark-mode-link-color: #6cb6ff; } html { background-color: #121212; } body { filter: invert(1) hue-rotate(180deg); transition: filter 0.2s ease-in-out; } img, video, iframe, canvas, svg, .icon, [style*="background-image"], .media, .thumb, .avatar, media, path { filter: invert(1) hue-rotate(180deg); } a { color: var(--dark-mode-link-color) !important; } button, input[type="button"], input[type="submit"] { background-color: #333 !important; color: #eee !important; } `; state.styleElement = GM_addStyle(darkCss); state.isDarkApplied = true; console.log('Efficient Dark Mode: Full styles applied.'); } /** * Removes the full dark mode styles. */ function removeDarkStyles() { if (!state.isDarkApplied || !state.styleElement) return; state.styleElement.remove(); state.styleElement = null; state.isDarkApplied = false; console.log('Efficient Dark Mode: Full styles removed.'); } /** * Creates a "black curtain" overlay to prevent the initial "white flash". */ function applyPreemptiveCurtain() { if (state.preemptiveCurtain) return; const curtain = document.createElement('div'); curtain.id = 'dark-mode-preemptive-curtain'; curtain.style.cssText = ` position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: rgb(18 18 18 / 95%); z-index: 2147483647; pointer-events: none; `; document.documentElement.appendChild(curtain); state.preemptiveCurtain = curtain; } /** * Removes the pre-emptive curtain. */ function removePreemptiveCurtain() { if (!state.preemptiveCurtain) return; state.preemptiveCurtain.remove(); state.preemptiveCurtain = null; } /** * Toggles the theme on and off for the current session. */ function toggleTheme() { if (state.isDarkApplied) { removeDarkStyles(); } else { applyDarkStyles(); } } /** * Registers menu commands for the user script manager. */ function registerMenuCommands() { const siteStatus = state.disabledSites.includes(window.location.hostname) ? 'Enable' : 'Disable'; GM_registerMenuCommand(`${siteStatus} Dark Mode for this site`, () => { const hostname = window.location.hostname; const index = state.disabledSites.indexOf(hostname); if (index > -1) { state.disabledSites.splice(index, 1); } else { state.disabledSites.push(hostname); } GM_setValue(STORAGE_KEYS.DISABLED_SITES, JSON.stringify(state.disabledSites)); alert(`Dark mode for ${hostname} has been ${index > -1 ? 'enabled' : 'disabled'}. Please reload the page.`); }); GM_registerMenuCommand(`Set Theme Preference (Current: ${state.userThemePref})`, () => { const modes = ['auto', 'dark', 'light']; let nextIndex = (modes.indexOf(state.userThemePref) + 1) % modes.length; state.userThemePref = modes[nextIndex]; GM_setValue(STORAGE_KEYS.THEME_STATE, state.userThemePref); alert(`Dark mode preference set to "${state.userThemePref}". Please reload the page.`); }); } /** * The main function to initialize the script logic. */ function main() { const shouldRun = (state.userThemePref === 'dark' || (state.userThemePref === 'auto' && userPrefersDark())); const isSiteDisabled = state.disabledSites.includes(window.location.hostname); if (shouldRun && !isSiteDisabled) { applyPreemptiveCurtain(); let hasChecked = false; const verifyAndApplyTheme = async () => { if (hasChecked) return; hasChecked = true; const siteIsNativelyDark = await isSiteAlreadyDark(); if (siteIsNativelyDark) { removePreemptiveCurtain(); console.log("Efficient Dark Mode: Native dark theme detected. No action needed."); } else { if (document.body && document.body.style.filter === 'none') { document.body.style.filter = ''; } applyDarkStyles(); setTimeout(() => { removePreemptiveCurtain(); }, 300); } }; window.addEventListener('load', verifyAndApplyTheme); setTimeout(verifyAndApplyTheme, 3000); } } // --- Event Listeners and Initialization --- document.addEventListener('keydown', (e) => { if (e.ctrlKey && e.key.toLowerCase() === 'd') { e.preventDefault(); toggleTheme(); } }); main(); registerMenuCommands(); })();