Dark Mode Auto Toggle

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

当前为 2025-01-19 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Dark Mode Auto Toggle
  3. // @version 0.1.1
  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. const bgcolor = window.getComputedStyle(document.body).backgroundColor;
  65.  
  66. // Convert rgb/rgba to array of values
  67. const [r, g, b] = bgcolor.match(/\d+/g).map(Number);
  68.  
  69. // Calculate relative luminance using sRGB formula
  70. // If luminance < 0.5, likely dark mode
  71. const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
  72. return luminance < 0.5;
  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. // Check computed colors
  90. const isDarkByColor = isDarkMode();
  91.  
  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. return systemPrefersDark || isDarkByColor || isDarkByClass || isDarkByAttribute;
  97. }
  98.  
  99. // Initialize darkMode based on stored preference or page state
  100. const pageIsDark = isPageDark();
  101.  
  102. // Create toggle button
  103. const button = document.createElement('button');
  104. button.innerHTML = pageIsDark ? '☼' : '☽';
  105. button.style.position = 'fixed';
  106. button.style.top = '5px';
  107. button.style.right = '10px';
  108. button.style.zIndex = '1000';
  109. button.style.background = 'none';
  110. button.style.border = 'none';
  111. button.style.fontSize = '24px';
  112. button.style.cursor = 'pointer';
  113. button.style.color = 'inherit';
  114.  
  115. // Function to get sunrise/sunset times
  116. async function getSunTimes() {
  117. // approximate times
  118. const now = new Date();
  119. return {
  120. sunrise: new Date(now.setHours(8, 0, 0, 0)),
  121. sunset: new Date(now.setHours(18, 0, 0, 0))
  122. };
  123. }
  124.  
  125. // Function to toggle dark mode
  126. function toggleDarkMode(forceDark) {
  127. if (forceDark !== undefined) {
  128. // Auto dark mode (sunrise/sunset)
  129. if (pageIsDark) {
  130. // If page is already dark, don't change anything
  131. return;
  132. }
  133. darkMode = forceDark;
  134. } else {
  135. // Manual toggle - always allow regardless of pageIsDark
  136. darkMode = !darkMode;
  137. }
  138.  
  139. if (darkMode) {
  140. document.head.appendChild(darkStyle);
  141. button.innerHTML = pageIsDark ? '☽' : '☼';
  142. } else {
  143. if (darkStyle.parentNode) {
  144. document.head.removeChild(darkStyle);
  145. }
  146. button.innerHTML = pageIsDark ? '☼' : '☽';
  147. }
  148. }
  149.  
  150. // Function to check and update dark mode based on time
  151. async function updateDarkMode() {
  152. const sunTimes = await getSunTimes();
  153. const now = new Date();
  154. const isDark = now < sunTimes.sunrise || now > sunTimes.sunset;
  155. toggleDarkMode(isDark);
  156. }
  157.  
  158. // Add settings button
  159. const settingsButton = document.createElement('button');
  160. settingsButton.innerHTML = '⚙';
  161. settingsButton.style.position = 'fixed';
  162. settingsButton.style.top = '40px';
  163. settingsButton.style.right = '15px';
  164. settingsButton.style.zIndex = '1000';
  165. settingsButton.style.background = 'none';
  166. settingsButton.style.border = 'none';
  167. settingsButton.style.fontSize = '12px';
  168. settingsButton.style.cursor = 'pointer';
  169. settingsButton.style.color = 'inherit';
  170.  
  171. // Settings panel to reset location
  172. settingsButton.addEventListener('click', () => {
  173. if (confirm('Reset location settings?')) {
  174. GM_setValue('userLocation', null);
  175. location.reload();
  176. }
  177. });
  178.  
  179. // Initial update
  180. updateDarkMode();
  181.  
  182. // Update every half hour (1800000)
  183. setInterval(updateDarkMode, 1800000);
  184.  
  185. // Manual toggle still works
  186. button.addEventListener('click', () => toggleDarkMode());
  187.  
  188. // Add buttons to page
  189. document.body.appendChild(button);
  190. })();