Dark Mode Auto Toggle

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

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Dark Mode Auto Toggle
// @version      0.0.5
// @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',
            'duckduckgo.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);
})();