AMOLED Dark Mode

Immersive dark mode for AMOLED screens

  1. // ==UserScript==
  2. // @name AMOLED Dark Mode
  3. // @namespace AMOLEDDark
  4. // @description Immersive dark mode for AMOLED screens
  5. // @version 1.0
  6. // @author moony
  7. // @match *://*/*
  8. // @grant GM_setValue
  9. // @grant GM_getValue
  10. // @run-at document-start
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. const store = {
  18. get: () => {
  19. try { return GM_getValue('amoledDarkLevel', 0); }
  20. catch (e) { return parseInt(localStorage.getItem('amoledDarkLevel') || 0); }
  21. },
  22. set: (v) => {
  23. try { GM_setValue('amoledDarkLevel', v); }
  24. catch (e) { localStorage.setItem('amoledDarkLevel', v); }
  25. }
  26. };
  27.  
  28. let darkLevel = store.get(),
  29. MAX_LEVELS = 4,
  30. styleEl = null,
  31. indicator = null;
  32.  
  33. // Apply at document start
  34. if(document.documentElement) {
  35. styleEl = document.createElement('style');
  36. styleEl.className = 'amoled-style';
  37. document.documentElement.appendChild(styleEl);
  38. applyLevel(darkLevel);
  39. }
  40.  
  41. // Create indicator when body is available
  42. document.addEventListener('DOMContentLoaded', () => {
  43. updateIndicator(darkLevel);
  44. });
  45.  
  46. // Debounced hotkey handler
  47. let keyTimer;
  48. document.addEventListener('keydown', e => {
  49. if (e.ctrlKey && e.key === '²') {
  50. if (keyTimer) return;
  51. keyTimer = setTimeout(() => { keyTimer = null; }, 300);
  52.  
  53. darkLevel = (darkLevel + 1) % (MAX_LEVELS + 1);
  54. store.set(darkLevel);
  55. applyLevel(darkLevel);
  56. updateIndicator(darkLevel);
  57. }
  58. });
  59.  
  60. function applyLevel(level) {
  61. if (!styleEl) return;
  62.  
  63. if (level === 0) {
  64. styleEl.textContent = '';
  65. return;
  66. }
  67.  
  68. let css = `*{background-color:#000!important;color:#fff!important;border-color:#333!important}
  69. ::-webkit-scrollbar{width:3px!important;height:3px!important}
  70. ::-webkit-scrollbar-thumb{background:#333!important;border-radius:3px!important}
  71. ::-webkit-scrollbar-track{background:#000!important}`;
  72.  
  73. if (level >= 2) css += 'aside,nav,footer,.sidebar,.ads,.banner,.menu,.navigation,.footer{display:none!important}';
  74. if (level >= 3) css += 'img,svg,video,iframe,canvas,button:not(.essential){display:none!important}';
  75.  
  76. if (level >= 4) {
  77. css += 'body>*:not(#amoled-indicator){opacity:0.3!important} main,article,.content,#content,.main-content,.post,article *{opacity:1!important}';
  78. }
  79.  
  80. styleEl.textContent = css;
  81. }
  82.  
  83. function updateIndicator(level) {
  84. if (!document.body) return;
  85.  
  86. if (!indicator) {
  87. indicator = document.createElement('div');
  88. indicator.id = 'amoled-indicator';
  89. indicator.style.cssText = 'position:fixed;bottom:10px;right:10px;background:#000;color:#fff;padding:5px;border-radius:3px;z-index:9999;font-size:12px;transition:opacity 0.3s;opacity:0.6;';
  90. indicator.addEventListener('mouseover', () => indicator.style.opacity = '1');
  91. indicator.addEventListener('mouseout', () => indicator.style.opacity = '0.6');
  92. document.body.appendChild(indicator);
  93. }
  94.  
  95. const messages = ['Off (Ctrl+² to activate)', 'Pure Black', 'Distraction-Free', 'Text-Only', 'Focus Mode'];
  96. indicator.textContent = `AMOLED: ${messages[level]}`;
  97.  
  98. // Auto-hide indicator after 3 seconds
  99. setTimeout(() => {
  100. if (indicator) indicator.style.opacity = '0';
  101. setTimeout(() => {
  102. if (indicator && indicator.style.opacity === '0')
  103. indicator.style.display = 'none';
  104. }, 300);
  105. }, 3000);
  106. }
  107. })();