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