Ultimate Night Mode

Change websites to pure black background with customization options

// ==UserScript==
// @name         Ultimate Night Mode
// @namespace    (link unavailable)
// @version      0.13
// @description  Change websites to pure black background with customization options
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @run-at       document-start
// ==/UserScript==

(function () {
  'use strict';

  // Load settings
  const cfg = {
    bgColor: GM_getValue("bgColor", "#000000"),
    textColor: GM_getValue("textColor", "#D3D3D3"), // Light Gray
    linkColor: GM_getValue("linkColor", "#1E90FF"), // Bright Blue
    visitedLinkColor: GM_getValue("visitedLinkColor", "#551A8B"), // Purple
    imgBrightness: GM_getValue("imgBrightness", 0.8),
    imgContrast: GM_getValue("imgContrast", 1.2),
    bgTransparency: GM_getValue("bgTransparency", 1),
    disableAllExceptBg: GM_getValue("disableAllExceptBg", false) // New option to disable all except background color
  };

  // CSS styles
  const css = `
    html, body {
      background-color: ${hexToRgba(cfg.bgColor, cfg.bgTransparency)} !important;
      ${cfg.disableAllExceptBg ? '' : `color: ${cfg.textColor} !important;`}
    }
    ${cfg.disableAllExceptBg ? '' : `
      * {
        background-color: rgba(0, 0, 0, 0.5) !important;
        border-color: #444444 !important;
      }
      a {
        color: ${cfg.linkColor} !important;
      }
      a:visited {
        color: ${cfg.visitedLinkColor} !important;
      }
      img.content-image {
        filter: brightness(${cfg.imgBrightness}) contrast(${cfg.imgContrast});
      }
      video, .html5-video-container video {
        filter: none !important;
      }
    `}
  `;

  // Apply styles
  const style = document.createElement('style');
  style.type = 'text/css';
  style.appendChild(document.createTextNode(css));
  document.head.appendChild(style);

  // Mutation observer to handle dynamic content
  const observer = new MutationObserver(function () {
    document.documentElement.style.backgroundColor = hexToRgba(cfg.bgColor, cfg.bgTransparency);
    document.body.style.backgroundColor = hexToRgba(cfg.bgColor, cfg.bgTransparency);
  });

  // Limit the observer to the body to avoid performance issues
  observer.observe(document.body, { childList: true, subtree: true });

  // Create configuration window
  function openConfigWindow() {
    const div = document.createElement('div');
    div.style.position = 'fixed';
    div.style.top = '10%';
    div.style.left = '50%';
    div.style.transform = 'translateX(-50%)';
    div.style.backgroundColor = '#333';
    div.style.color = '#fff';
    div.style.padding = '20px';
    div.style.border = '1px solid #444';
    div.style.zIndex = '10000';
    div.innerHTML = `
      <h2>Customize Theme</h2>
      <label>BG Color: <input type="color" id="bgColor" value="${cfg.bgColor}"></label><br>
      <label>Text: <input type="color" id="textColor" value="${cfg.textColor}"></label><br>
      <label>Link: <input type="color" id="linkColor" value="${cfg.linkColor}"></label><br>
      <label>Visited: <input type="color" id="visitedLinkColor" value="${cfg.visitedLinkColor}"></label><br>
      <label>Brightness: <input type="range" id="imgBrightness" min="0" max="1" step="0.1" value="${cfg.imgBrightness}"></label><br>
      <label>Contrast: <input type="range" id="imgContrast" min="1" max="2" step="0.1" value="${cfg.imgContrast}"></label><br>
      <label>Transparency: <input type="range" id="bgTransparency" min="0" max="1" step="0.1" value="${cfg.bgTransparency}"></label><br>
      <label>Disable All Except BG Color: <input type="checkbox" id="disableAllExceptBg" ${cfg.disableAllExceptBg ? 'checked' : ''}></label><br>
      <button id="saveConfig">Save</button>
      <button id="closeConfig">Close</button>
    `;
    document.body.appendChild(div);

    // Save configuration
    document.getElementById('saveConfig').addEventListener('click', () => {
      GM_setValue("bgColor", document.getElementById('bgColor').value);
      GM_setValue("textColor", document.getElementById('textColor').value);
      GM_setValue("linkColor", document.getElementById('linkColor').value);
      GM_setValue("visitedLinkColor", document.getElementById('visitedLinkColor').value);
      GM_setValue("imgBrightness", parseFloat(document.getElementById('imgBrightness').value));
      GM_setValue("imgContrast", parseFloat(document.getElementById('imgContrast').value));
      GM_setValue("bgTransparency", parseFloat(document.getElementById('bgTransparency').value));
      GM_setValue("disableAllExceptBg", document.getElementById('disableAllExceptBg').checked);
      location.reload();
    });

    document.getElementById('closeConfig').addEventListener('click', () => {
      div.remove();
    });
  }

  // Convert hex to rgba
  function hexToRgba(hex, alpha) {
    let r = 0, g = 0, b = 0;
    if (hex.length === 4) {
      r = parseInt(hex[1] + hex[1], 16);
      g = parseInt(hex[2] + hex[2], 16);
      b = parseInt(hex[3] + hex[3], 16);
    } else if (hex.length === 7) {
      r = parseInt(hex.slice(1, 3), 16);
      g = parseInt(hex.slice(3, 5), 16);
      b = parseInt(hex.slice(5, 7), 16);
    }
    return `rgba(${r},${g},${b},${alpha})`;
  }

  // Menu command to open configuration window
  if (typeof GM_registerMenuCommand !== "undefined") {
    GM_registerMenuCommand("Customize Theme", openConfigWindow, "C");
  }
})();