Dark Mode Auto Toggle

Enable dark mode with a toggle button and automatically based on location

目前為 2025-01-16 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Dark Mode Auto Toggle
// @version      0.0.4
// @description  Enable dark mode with a toggle button and automatically based on location
// @namespace    https://greasyfork.org/en/users/28298
// @author       https://greasyfork.org/en/users/28298
// @homepage     https://greasyfork.org/en/scripts/523690
// @license      MIT
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==

// @grant        none
// original author: Michael Wang https://greasyfork.org/en/scripts/472251-dark-mode/code
// with help of claude ai

(function() {
    'use strict';

    // Create style element for dark mode
    const darkStyle = document.createElement('style');
    darkStyle.textContent = `
        html {
            filter: invert(1) hue-rotate(180deg) contrast(0.8);
        }
        /** reverse filter for media elements */
        img, video, picture, canvas, iframe, embed {
            filter: invert(1) hue-rotate(180deg);
        }
    `;

    // Initialize based on page's current state
    let darkMode = false; // Start with no filter

    // Function to detect if page is already dark
    function isPageDark() {
        const url = window.location.hostname;
        ////////////////////////////////////////////////////////////////////
        // manually tell domain's mode, because auto detection would fail
        const domainModes = {
            'claude.ai': 'bright',
            'chat.openai.com': 'dark',
            'greasyfork.org': 'bright',
        };
        ///////////////////////////////////////////////////////////////////
        // Check if domain is in our list
        for (const domain in domainModes) {
            if (url.includes(domain)) {
                return domainModes[domain] === 'dark';
            }
        }

        // easy solution, but not always accurate
        // return document.documentElement.classList.contains('dark')

        const bodyBg = window.getComputedStyle(document.body).backgroundColor;
        const htmlBg = window.getComputedStyle(document.documentElement).backgroundColor;

        // Convert RGB/RGBA to brightness value (0-255)
        function getBrightness(color) {
            const rgb = color.match(/\d+/g);
            if (!rgb) return 255; // Default to light if can't detect
            // Use perceived brightness formula
            return (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
        }

        const bodyBrightness = getBrightness(bodyBg);
        const htmlBrightness = getBrightness(htmlBg);

        // Use the darker of the two values
        const brightness = Math.min(bodyBrightness, htmlBrightness);
        return brightness < 128; // Consider it dark if brightness is less than middle gray
    }
    // Initialize darkMode based on stored preference or page state
    const pageIsDark = isPageDark();

    // Create toggle button
    const button = document.createElement('button');
    button.innerHTML = pageIsDark ? '☼' : '☽';
    button.style.position = 'fixed';
    button.style.top = '5px';
    button.style.right = '10px';
    button.style.zIndex = '1000';
    button.style.background = 'none';
    button.style.border = 'none';
    button.style.fontSize = '24px';
    button.style.cursor = 'pointer';
    button.style.color = 'inherit';

    // Function to get location and store it
    async function setupLocation() {
        let location = GM_getValue('userLocation');

        if (!location) {
            try {
                const position = await new Promise((resolve, reject) => {
                    navigator.geolocation.getCurrentPosition(resolve, reject);
                });

                location = {
                    latitude: position.coords.latitude,
                    longitude: position.coords.longitude,
                    timestamp: Date.now()
                };

                GM_setValue('userLocation', location);
                console.log('Location saved successfully');
            } catch (error) {
                console.error('Error getting location:', error);
                // Default to a fallback location (e.g., UTC+0)
                location = {
                    latitude: 51.5074,  // London coordinates as fallback
                    longitude: -0.1278,
                    timestamp: Date.now()
                };
                GM_setValue('userLocation', location);
            }
        }
        return location;
    }

    // Function to get sunrise/sunset times using stored location
    async function getSunTimes() {
        try {
            const location = await setupLocation();
            const response = await fetch(
                `https://api.sunrise-sunset.org/json?lat=${location.latitude}&lng=${location.longitude}&formatted=0`
            );
            const data = await response.json();
            return {
                sunrise: new Date(data.results.sunrise),
                sunset: new Date(data.results.sunset)
            };
        } catch (error) {
            console.error('Error getting sun times:', error);
            // Fallback to approximate times
            const now = new Date();
            return {
                sunrise: new Date(now.setHours(6, 0, 0, 0)),
                sunset: new Date(now.setHours(18, 0, 0, 0))
            };
        }
    }

    // Function to toggle dark mode
    function toggleDarkMode(forceDark) {
        if (forceDark !== undefined) {
            // Auto dark mode (sunrise/sunset)
            if (pageIsDark) {
                // If page is already dark, don't change anything
                return;
            }
            darkMode = forceDark;
        } else {
            // Manual toggle - always allow regardless of pageIsDark
            darkMode = !darkMode;
        }

        if (darkMode) {
            document.head.appendChild(darkStyle);
            button.innerHTML = pageIsDark ? '☽' : '☼';
        } else {
            if (darkStyle.parentNode) {
                document.head.removeChild(darkStyle);
            }
            button.innerHTML = pageIsDark ? '☼' : '☽';
        }
    }

    // Function to check and update dark mode based on time
    async function updateDarkMode() {
        const sunTimes = await getSunTimes();
        const now = new Date();
        const isDark = now < sunTimes.sunrise || now > sunTimes.sunset;
        toggleDarkMode(isDark);
    }

    // Add settings button
    const settingsButton = document.createElement('button');
    settingsButton.innerHTML = '⚙';
    settingsButton.style.position = 'fixed';
    settingsButton.style.top = '30px';
    settingsButton.style.right = '15px';
    settingsButton.style.zIndex = '1000';
    settingsButton.style.background = 'none';
    settingsButton.style.border = 'none';
    settingsButton.style.fontSize = '12px';
    settingsButton.style.cursor = 'pointer';
    settingsButton.style.color = 'inherit';

    // Settings panel to reset location
    settingsButton.addEventListener('click', () => {
        if (confirm('Reset location settings?')) {
            GM_setValue('userLocation', null);
            location.reload();
        }
    });

    // Initial update
    updateDarkMode();

    // Update every hour
    setInterval(updateDarkMode, 3600000);

    // Manual toggle still works
    button.addEventListener('click', () => toggleDarkMode());

    // Add buttons to page
    document.body.appendChild(button);
    document.body.appendChild(settingsButton);
})();