Dark Mode Auto Toggle

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

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

  1. // ==UserScript==
  2. // @name Dark Mode Auto Toggle
  3. // @version 0.0.4
  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. // Create style element for dark mode
  22. const darkStyle = document.createElement('style');
  23. darkStyle.textContent = `
  24. html {
  25. filter: invert(1) hue-rotate(180deg) contrast(0.8);
  26. }
  27. /** reverse filter for media elements */
  28. img, video, picture, canvas, iframe, embed {
  29. filter: invert(1) hue-rotate(180deg);
  30. }
  31. `;
  32.  
  33. // Initialize based on page's current state
  34. let darkMode = false; // Start with no filter
  35.  
  36. // Function to detect if page is already dark
  37. function isPageDark() {
  38. const url = window.location.hostname;
  39. ////////////////////////////////////////////////////////////////////
  40. // manually tell domain's mode, because auto detection would fail
  41. const domainModes = {
  42. 'claude.ai': 'bright',
  43. 'chat.openai.com': 'dark',
  44. 'greasyfork.org': 'bright',
  45. };
  46. ///////////////////////////////////////////////////////////////////
  47. // Check if domain is in our list
  48. for (const domain in domainModes) {
  49. if (url.includes(domain)) {
  50. return domainModes[domain] === 'dark';
  51. }
  52. }
  53.  
  54. // easy solution, but not always accurate
  55. // return document.documentElement.classList.contains('dark')
  56.  
  57. const bodyBg = window.getComputedStyle(document.body).backgroundColor;
  58. const htmlBg = window.getComputedStyle(document.documentElement).backgroundColor;
  59.  
  60. // Convert RGB/RGBA to brightness value (0-255)
  61. function getBrightness(color) {
  62. const rgb = color.match(/\d+/g);
  63. if (!rgb) return 255; // Default to light if can't detect
  64. // Use perceived brightness formula
  65. return (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
  66. }
  67.  
  68. const bodyBrightness = getBrightness(bodyBg);
  69. const htmlBrightness = getBrightness(htmlBg);
  70.  
  71. // Use the darker of the two values
  72. const brightness = Math.min(bodyBrightness, htmlBrightness);
  73. return brightness < 128; // Consider it dark if brightness is less than middle gray
  74. }
  75. // Initialize darkMode based on stored preference or page state
  76. const pageIsDark = isPageDark();
  77.  
  78. // Create toggle button
  79. const button = document.createElement('button');
  80. button.innerHTML = pageIsDark ? '☼' : '☽';
  81. button.style.position = 'fixed';
  82. button.style.top = '5px';
  83. button.style.right = '10px';
  84. button.style.zIndex = '1000';
  85. button.style.background = 'none';
  86. button.style.border = 'none';
  87. button.style.fontSize = '24px';
  88. button.style.cursor = 'pointer';
  89. button.style.color = 'inherit';
  90.  
  91. // Function to get location and store it
  92. async function setupLocation() {
  93. let location = GM_getValue('userLocation');
  94.  
  95. if (!location) {
  96. try {
  97. const position = await new Promise((resolve, reject) => {
  98. navigator.geolocation.getCurrentPosition(resolve, reject);
  99. });
  100.  
  101. location = {
  102. latitude: position.coords.latitude,
  103. longitude: position.coords.longitude,
  104. timestamp: Date.now()
  105. };
  106.  
  107. GM_setValue('userLocation', location);
  108. console.log('Location saved successfully');
  109. } catch (error) {
  110. console.error('Error getting location:', error);
  111. // Default to a fallback location (e.g., UTC+0)
  112. location = {
  113. latitude: 51.5074, // London coordinates as fallback
  114. longitude: -0.1278,
  115. timestamp: Date.now()
  116. };
  117. GM_setValue('userLocation', location);
  118. }
  119. }
  120. return location;
  121. }
  122.  
  123. // Function to get sunrise/sunset times using stored location
  124. async function getSunTimes() {
  125. try {
  126. const location = await setupLocation();
  127. const response = await fetch(
  128. `https://api.sunrise-sunset.org/json?lat=${location.latitude}&lng=${location.longitude}&formatted=0`
  129. );
  130. const data = await response.json();
  131. return {
  132. sunrise: new Date(data.results.sunrise),
  133. sunset: new Date(data.results.sunset)
  134. };
  135. } catch (error) {
  136. console.error('Error getting sun times:', error);
  137. // Fallback to approximate times
  138. const now = new Date();
  139. return {
  140. sunrise: new Date(now.setHours(6, 0, 0, 0)),
  141. sunset: new Date(now.setHours(18, 0, 0, 0))
  142. };
  143. }
  144. }
  145.  
  146. // Function to toggle dark mode
  147. function toggleDarkMode(forceDark) {
  148. if (forceDark !== undefined) {
  149. // Auto dark mode (sunrise/sunset)
  150. if (pageIsDark) {
  151. // If page is already dark, don't change anything
  152. return;
  153. }
  154. darkMode = forceDark;
  155. } else {
  156. // Manual toggle - always allow regardless of pageIsDark
  157. darkMode = !darkMode;
  158. }
  159.  
  160. if (darkMode) {
  161. document.head.appendChild(darkStyle);
  162. button.innerHTML = pageIsDark ? '☽' : '☼';
  163. } else {
  164. if (darkStyle.parentNode) {
  165. document.head.removeChild(darkStyle);
  166. }
  167. button.innerHTML = pageIsDark ? '☼' : '☽';
  168. }
  169. }
  170.  
  171. // Function to check and update dark mode based on time
  172. async function updateDarkMode() {
  173. const sunTimes = await getSunTimes();
  174. const now = new Date();
  175. const isDark = now < sunTimes.sunrise || now > sunTimes.sunset;
  176. toggleDarkMode(isDark);
  177. }
  178.  
  179. // Add settings button
  180. const settingsButton = document.createElement('button');
  181. settingsButton.innerHTML = '⚙';
  182. settingsButton.style.position = 'fixed';
  183. settingsButton.style.top = '30px';
  184. settingsButton.style.right = '15px';
  185. settingsButton.style.zIndex = '1000';
  186. settingsButton.style.background = 'none';
  187. settingsButton.style.border = 'none';
  188. settingsButton.style.fontSize = '12px';
  189. settingsButton.style.cursor = 'pointer';
  190. settingsButton.style.color = 'inherit';
  191.  
  192. // Settings panel to reset location
  193. settingsButton.addEventListener('click', () => {
  194. if (confirm('Reset location settings?')) {
  195. GM_setValue('userLocation', null);
  196. location.reload();
  197. }
  198. });
  199.  
  200. // Initial update
  201. updateDarkMode();
  202.  
  203. // Update every hour
  204. setInterval(updateDarkMode, 3600000);
  205.  
  206. // Manual toggle still works
  207. button.addEventListener('click', () => toggleDarkMode());
  208.  
  209. // Add buttons to page
  210. document.body.appendChild(button);
  211. document.body.appendChild(settingsButton);
  212. })();