Dark Mode Auto Toggle

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

目前为 2025-01-20 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Dark Mode Auto Toggle
  3. // @version 0.1.3
  4. // @description Enable dark mode with a toggle button and automatically based on location
  5. // @namespace https://greasyfork.org/en/users/28298
  6. // @author https://greasyfork.org/en/users/28298
  7. // @homepage https://greasyfork.org/en/scripts/523690
  8. // @license MIT
  9. // @match *://*/*
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // ==/UserScript==
  13.  
  14. // @grant none
  15. // original author: Michael Wang https://greasyfork.org/en/scripts/472251-dark-mode/code
  16. // with help of claude ai
  17.  
  18. (function() {
  19. 'use strict';
  20.  
  21. //////////////////////////////////////////////////////////////////////////
  22. // manually tell domain's original mode, because auto detection would fail
  23. const domainModes = {
  24. 'claude.ai' : 'bright',
  25. 'chat.openai.com' : 'dark',
  26. 'duckduckgo.com' : 'dark',
  27. 'www.google.com' : 'bright',
  28. 'youtube.com' : 'dark',
  29. 'greasyfork.org' : 'bright',
  30. '192.168.1.1' : 'dark',
  31. '192.168.1.2' : 'dark',
  32. '192.168.1.3' : 'dark',
  33. '127.0.0.1' : 'dark',
  34. };
  35. /////////////////////////////////////////////////////////////////////////
  36.  
  37. // Create style element for dark mode
  38. const darkStyle = document.createElement('style');
  39. darkStyle.textContent = `
  40. html {
  41. filter: invert(1) hue-rotate(180deg) contrast(0.8);
  42. }
  43. /** reverse filter for media elements */
  44. img, video, picture, canvas, iframe, embed {
  45. filter: invert(1) hue-rotate(180deg);
  46. }
  47. `;
  48.  
  49. // Initialize based on page's current state
  50. let darkMode = false; // Start with no filter
  51.  
  52. // Function to detect if page is already dark
  53. function isPageDark() {
  54. const url = window.location.hostname;
  55. // Check if domain is in our list
  56. for (const domain in domainModes) {
  57. if (url.includes(domain)) {
  58. return domainModes[domain] === 'dark';
  59. }
  60. }
  61.  
  62. // auto detection
  63. function isDarkMode() {
  64. try {
  65. const bgcolor = window.getComputedStyle(document.body).backgroundColor;
  66. const rgb = color.match(/\d+/g);
  67. if (!rgb) return 255 < 128; // Default to light if can't detect
  68. // Use perceived brightness formula
  69. return (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000 < 128;
  70. } catch (e) {
  71. return false;
  72. }
  73. }
  74. function checkDarkModeClasses() {
  75. const html = document.documentElement;
  76. const body = document.body;
  77.  
  78. const darkClasses = ['dark', 'dark-mode', 'dark-theme', 'theme-dark'];
  79. return darkClasses.some(className =>
  80. html.classList.contains(className) || body.classList.contains(className)
  81. );
  82. }
  83. function checkDarkModeAttributes() {
  84. const html = document.documentElement;
  85.  
  86. return html.getAttribute('data-theme') === 'dark' ||
  87. html.getAttribute('data-color-mode') === 'dark';
  88. }
  89.  
  90. // Check computed colors
  91. const isDarkByColor = isDarkMode();
  92. // Check classes and attributes
  93. const isDarkByClass = checkDarkModeClasses();
  94. const isDarkByAttribute = checkDarkModeAttributes();
  95. // Site is likely in dark mode if any of these are true
  96.  
  97. return isDarkByClass || isDarkByAttribute || isDarkByColor;
  98. }
  99.  
  100. // Initialize darkMode based on stored preference or page state
  101. const pageIsDark = isPageDark();
  102.  
  103. // Create toggle button
  104. const button = document.createElement('button');
  105. button.innerHTML = pageIsDark ? '☼' : '☽';
  106. button.style.position = 'fixed';
  107. button.style.top = '5px';
  108. button.style.right = '10px';
  109. button.style.zIndex = '1000';
  110. button.style.background = 'none';
  111. button.style.border = 'none';
  112. button.style.fontSize = '24px';
  113. button.style.cursor = 'pointer';
  114. button.style.color = 'inherit';
  115.  
  116. // Function to get sunrise/sunset times
  117. async function getSunTimes() {
  118. // approximate times
  119. const now = new Date();
  120. return {
  121. sunrise: new Date(now.setHours(8, 0, 0, 0)),
  122. sunset: new Date(now.setHours(18, 0, 0, 0))
  123. };
  124. }
  125.  
  126. // Function to toggle dark mode
  127. function toggleDarkMode(forceDark) {
  128. if (forceDark !== undefined) {
  129. // Auto dark mode (sunrise/sunset)
  130. if (pageIsDark) {
  131. // If page is already dark, don't change anything
  132. return;
  133. }
  134. darkMode = forceDark;
  135. } else {
  136. // Manual toggle - always allow regardless of pageIsDark
  137. darkMode = !darkMode;
  138. }
  139.  
  140. if (darkMode) {
  141. document.head.appendChild(darkStyle);
  142. button.innerHTML = pageIsDark ? '☽' : '☼';
  143. } else {
  144. if (darkStyle.parentNode) {
  145. document.head.removeChild(darkStyle);
  146. }
  147. button.innerHTML = pageIsDark ? '☼' : '☽';
  148. }
  149. }
  150.  
  151. // Function to check and update dark mode based on time
  152. async function updateDarkMode() {
  153. const sunTimes = await getSunTimes();
  154. const now = new Date();
  155. const isDark = now < sunTimes.sunrise || now > sunTimes.sunset;
  156. toggleDarkMode(isDark);
  157. }
  158.  
  159. // Add settings button
  160. const settingsButton = document.createElement('button');
  161. settingsButton.innerHTML = '⚙';
  162. settingsButton.style.position = 'fixed';
  163. settingsButton.style.top = '40px';
  164. settingsButton.style.right = '15px';
  165. settingsButton.style.zIndex = '1000';
  166. settingsButton.style.background = 'none';
  167. settingsButton.style.border = 'none';
  168. settingsButton.style.fontSize = '12px';
  169. settingsButton.style.cursor = 'pointer';
  170. settingsButton.style.color = 'inherit';
  171.  
  172. // Settings panel to reset location
  173. settingsButton.addEventListener('click', () => {
  174. if (confirm('Reset location settings?')) {
  175. GM_setValue('userLocation', null);
  176. location.reload();
  177. }
  178. });
  179.  
  180. // Initial update
  181. updateDarkMode();
  182.  
  183. // Update every half hour (1800000)
  184. setInterval(updateDarkMode, 1800000);
  185.  
  186. // Manual toggle still works
  187. button.addEventListener('click', () => toggleDarkMode());
  188.  
  189. // Add buttons to page
  190. document.body.appendChild(button);
  191. })();