Bilibili 暗黑模式

启用 Bilibili 暗黑模式

目前為 2023-11-20 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Bilibili 暗黑模式
// @namespace    http://tampermonkey.net/
// @version      1.2.1
// @description  启用 Bilibili 暗黑模式
// @author       K
// @match        https://*.bilibili.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_addValueChangeListener

// @icon         https://www.google.com/s2/favicons?domain=www.bilibili.com
// @license MIT
// ==/UserScript==

(function () {
  "use strict";
  const DARK_MODE_BTN_ID = "dark-mode-btn";
  const USER_PREFERS_MODE_KEY = "user-prefers-mode"; // "dark" | "light"
  const SYSTEM_PREFERS_FIRST_KEY = "system-prefers-first"; // true | false
  const DARK_MODE_CLASS_NAME = "bili_dark";
  const DARK_MODE_BRIGHTNESS = 0.9;

  let containerSelectors;

  // Dom
  function getContainerSelectors() {
    const currentUrl = window.location.href;
    if (currentUrl.startsWith("https://www.bilibili.com/video/")) {
      return ".fixed-sidenav-storage";
    } else if (currentUrl.startsWith("https://t.bilibili.com/")) {
      return ".bili-dyn-sidebar";
    }
  }

  function createDarkModeBtn() {
    const currentUrl = window.location.href;
    const darkModeBtn = document.createElement("div");

    const darkModeText = document.createElement("span");
    const lightModeText = document.createElement("span");
    lightModeText.classList.add("light");
    lightModeText.textContent = "浅色模式";
    darkModeText.classList.add("dark");
    darkModeText.textContent = "深色模式";

    darkModeBtn.setAttribute("id", DARK_MODE_BTN_ID);
    darkModeBtn.appendChild(lightModeText);
    darkModeBtn.appendChild(darkModeText);

    if (currentUrl.startsWith("https://www.bilibili.com/video/")) {
      darkModeBtn.classList.add("video-page-btn");
    } else if (currentUrl.startsWith("https://t.bilibili.com/")) {
      darkModeBtn.classList.add("dynamic-page-btn");
    }
    darkModeBtn.addEventListener("click", (event) => {
      toggleDarkMode();
    });
    return darkModeBtn;
  }

  function mountDarkModeBtn(mutationList, observer) {
    if (!containerSelectors) {
      containerSelectors = getContainerSelectors();
    }
    const container = document.querySelector(containerSelectors);
    if (container) {
      const darkModeBtn = createDarkModeBtn();
      container.insertBefore(darkModeBtn, container.firstChild);
      observer.disconnect();
    }
  }

  function waitForMountDarkModeBtn() {
    const observer = new MutationObserver(mountDarkModeBtn);
    observer.observe(document.querySelector("#app")||document.body , {
      attributes: true,
      childList: true,
      subtree: false,
    });
  }

  // Uitl
  function hasDarkModeClass() {
    return document.documentElement.classList.contains(DARK_MODE_CLASS_NAME);
  }
  function setDarkMode() {
    GM_setValue(USER_PREFERS_MODE_KEY, "dark");
    document.documentElement.classList.toggle(DARK_MODE_CLASS_NAME, true);
  }

  function setLightMode() {
    GM_setValue(USER_PREFERS_MODE_KEY, "light");
    document.documentElement.classList.toggle(DARK_MODE_CLASS_NAME, false);
  }
  function toggleDarkMode() {
    if (hasDarkModeClass()) {
      setLightMode();
    } else {
      setDarkMode();
    }
  }
  function enableSystemPrefersFirst() {
    GM_setValue(SYSTEM_PREFERS_FIRST_KEY, true);
    handlePrefersColorSchemeChange();
  }
  function disableSystemPrefersFirst() {
    GM_setValue(SYSTEM_PREFERS_FIRST_KEY, false);
    const userPrefersMode = GM_getValue(USER_PREFERS_MODE_KEY);
    userPrefersMode === "dark" ? setDarkMode() : setLightMode();
  }
  function handlePrefersColorSchemeChange(event) {
    if (!event) {
      event = window.matchMedia("(prefers-color-scheme: dark)");
    }
    const isSystemFirst = GM_getValue(SYSTEM_PREFERS_FIRST_KEY);
    document.documentElement.classList.toggle(
      DARK_MODE_CLASS_NAME,
      isSystemFirst && event.matches
    );
  }

  // Init
  function initDarkMode() {
    const userPrefersMode = GM_getValue(USER_PREFERS_MODE_KEY);
    const isSystemFirst = GM_getValue(SYSTEM_PREFERS_FIRST_KEY);
    window
      .matchMedia("(prefers-color-scheme: dark)")
      .addEventListener("change", handlePrefersColorSchemeChange);
    if (isSystemFirst) {
      handlePrefersColorSchemeChange();
    } else {
      userPrefersMode === "dark" ? setDarkMode() : setLightMode();
    }
  }

  // Menu
  async function registerMenuCommand() {
    await GM_registerMenuCommand("深色模式", setDarkMode);
    await GM_registerMenuCommand("浅色模式", setLightMode);
    await GM_registerMenuCommand("开启跟随系统变化", enableSystemPrefersFirst);
    await GM_registerMenuCommand("关闭跟随系统变化", disableSystemPrefersFirst);
  }

  // Style
  function addDarkModeStyle() {
    const currentUrl = window.location.href;
    const style = document.createElement("style");
    // 基本暗黑模式样式
    style.textContent = `
    :root:not(.${DARK_MODE_CLASS_NAME}) {
    }
    :root.${DARK_MODE_CLASS_NAME} {
       --Ga0: #0d0d0e;
       --Ga0_s: #1e2022;
       --Ga0_t: #1e2022;
       --Ga1: #000;
       --Ga1_s: #232527;
       --Ga1_t: #232527;
       --Ga1_e: #232527;
       --Ga2: #2f3134;
       --Ga2_t: #2f3134;
       --Ga3: #46494d;
       --Ga3_t: #46494d;
       --Ga4: #5e6267;
       --Ga4_t: #5e6267;
       --Ga5: #757a81;
       --Ga5_t: #757a81;
       --Ga6: #8b9097;
       --Ga6_t: #8b9097;
       --Ga7: #a2a7ae;
       --Ga7_t: #a2a7ae;
       --Ga8: #b9bdc2;
       --Ga8_t: #b9bdc2;
       --Ga9: #d0d3d7;
       --Ga9_t: #d0d3d7;
       --Ga10: #e7e9eb;
       --Ga10_t: #e7e9eb;
       --Ga11: #242628;
       --Ga12: #1f2022;
       --Wh0: #17181a;
       --Wh0_t: #17181a;
       --Ba0: #000;
       --Ba0_s: #fff;
       --Ba0_t: #000;
       --Pi0: #26161c;
       --Pi1: #2f1a22;
       --Pi2: #472030;
       --Pi3: #76304b;
       --Pi4: #a73e65;
       --Pi5: #d44e7d;
       --Pi5_t: #d44e7d;
       --Pi6: #dc6d94;
       --Pi7: #e38caa;
       --Pi8: #ebabc1;
       --Pi9: #f2cad8;
       --Pi10: #fae9ef;
       --Ma0: #261525;
       --Ma1: #2e182d;
       --Ma2: #461c43;
       --Ma3: #72296c;
       --Ma4: #a13396;
       --Ma5: #cb41bb;
       --Ma6: #d462c7;
       --Ma7: #dd83d3;
       --Ma8: #e6a4de;
       --Ma9: #efc5ea;
       --Ma10: #f8e6f6;
       --Re0: #261314;
       --Re1: #2e1617;
       --Re2: #471a1c;
       --Re3: #742728;
       --Re4: #a63131;
       --Re5: #d1403e;
       --Re6: #d9615f;
       --Re7: #e18281;
       --Re8: #e9a3a2;
       --Re9: #f1c5c4;
       --Re10: #f9e5e5;
       --Or0: #28180f;
       --Or1: #301b10;
       --Or2: #4a230e;
       --Or3: #783610;
       --Or4: #a9490d;
       --Or5: #d66011;
       --Or6: #dd7c3a;
       --Or7: #e49764;
       --Or8: #ebb38d;
       --Or9: #f2ceb6;
       --Or10: #faeadf;
       --Ye0: #2a1e0f;
       --Ye1: #342410;
       --Ye2: #4d300b;
       --Ye3: #7c4c08;
       --Ye4: #ad6800;
       --Ye5: #db8700;
       --Ye6: #e19c2c;
       --Ye7: #e7b158;
       --Ye8: #eec584;
       --Ye9: #f4dab1;
       --Ye10: #faefdd;
       --Ly0: #2a2310;
       --Ly1: #332a11;
       --Ly2: #49390c;
       --Ly3: #745909;
       --Ly4: #a27c00;
       --Ly5: #cca000;
       --Ly6: #d5b02c;
       --Ly7: #dec158;
       --Ly8: #e7d184;
       --Ly9: #efe2b1;
       --Ly10: #f8f2dd;
       --Lg0: #19220f;
       --Lg1: #1e2911;
       --Lg2: #273c0e;
       --Lg3: #3c600f;
       --Lg4: #50840b;
       --Lg5: #67a70e;
       --Lg6: #81b638;
       --Lg7: #9cc562;
       --Lg8: #b6d58b;
       --Lg9: #d0e4b5;
       --Lg10: #ebf3df;
       --Gr0: #102017;
       --Gr1: #11271b;
       --Gr2: #123923;
       --Gr3: #175c34;
       --Gr4: #198042;
       --Gr5: #1fa251;
       --Gr6: #46b26f;
       --Gr7: #6dc28d;
       --Gr8: #93d2ab;
       --Gr9: #bae2c9;
       --Gr10: #e1f3e8;
       --Cy0: #0c1f20;
       --Cy1: #0d2627;
       --Cy2: #093739;
       --Cy3: #085b5c;
       --Cy4: #028080;
       --Cy5: #03a29f;
       --Cy6: #2fb2b0;
       --Cy7: #5ac2c0;
       --Cy8: #86d2d1;
       --Cy9: #b2e2e1;
       --Cy10: #ddf3f3;
       --Lb0: #0a1b23;
       --Lb1: #0b202a;
       --Lb2: #082d40;
       --Lb3: #064a69;
       --Lb4: #006996;
       --Lb5: #0087bd;
       --Lb6: #2c9cc8;
       --Lb7: #58b1d4;
       --Lb8: #84c5df;
       --Lb9: #b1dbeb;
       --Lb10: #ddeff6;
       --Bl0: #151826;
       --Bl1: #181c2f;
       --Bl2: #1f2748;
       --Bl3: #2e3c76;
       --Bl4: #3b53a8;
       --Bl5: #4b6bd4;
       --Bl6: #6a85db;
       --Bl7: #899ee3;
       --Bl8: #a9b8ea;
       --Bl9: #c8d2f2;
       --Bl10: #e7ebf9;
       --Pu0: #1d1628;
       --Pu1: #221a31;
       --Pu2: #31214c;
       --Pu3: #4e317d;
       --Pu4: #6d3fb1;
       --Pu5: #8c50e0;
       --Pu6: #a06ee5;
       --Pu7: #b48deb;
       --Pu8: #c8abf0;
       --Pu9: #dbc9f5;
       --Pu10: #f0e8fb;
       --Br0: #211d1b;
       --Br1: #282320;
       --Br2: #382f2a;
       --Br3: #59483f;
       --Br4: #7a6154;
       --Br5: #9a7c6a;
       --Br6: #ac9384;
       --Br7: #bda99e;
       --Br8: #cebfb7;
       --Br9: #e0d7d1;
       --Br10: #f2eeeb;
       --Si0: #212325;
       --Si1: #27292c;
       --Si2: #36393f;
       --Si3: #535962;
       --Si4: #6f7987;
       --Si5: #8c99aa;
       --Si6: #a0abb9;
       --Si7: #b4bcc7;
       --Si8: #c8ced6;
       --Si9: #dce0e5;
       --Si10: #f0f2f4;
       --Pi5_rgb: 212, 78, 125;
       --Pi1_rgb: 47, 26, 34;
       --Lb5_rgb: 0, 135, 189;
       --Lb1_rgb: 11, 32, 42;
       --Re5_rgb: 209, 64, 62;
       --Re1_rgb: 46, 22, 23;
       --Gr5_rgb: 31, 162, 81;
       --Gr1_rgb: 17, 39, 27;
       --Or5_rgb: 214, 96, 17;
       --Or1_rgb: 48, 27, 16;
       --Ye5_rgb: 219, 135, 0;
       --Ye1_rgb: 52, 36, 16;
       --Wh0_rgb: 23, 24, 26;
       --Ga0_rgb: 13, 13, 14;
       --Ga1_rgb: 0, 0, 0;
       --Ga11_rgb: 36, 38, 40;
       --Ga12_rgb: 31, 32, 34;
       --Wh0_u_rgb: 255, 255, 255;
       --Ga10_rgb: 231, 233, 235;
       --Ga7_rgb: 162, 167, 174;
       --Ga5_rgb: 117, 122, 129;
       --Ga3_rgb: 70, 73, 77;
       --Lb6_rgb: 44, 156, 200;
       --Ye6_rgb: 225, 156, 44;
       --Ga1_s_rgb: 35, 37, 39;
       --Ga2_rgb: 47, 49, 52;
       --Ga0_s_rgb: 30, 32, 34;
       --Ba0_rgb: 0, 0, 0;
       filter: brightness(${DARK_MODE_BRIGHTNESS});
    }
    .${DARK_MODE_CLASS_NAME} #${DARK_MODE_BTN_ID} span.light,#${DARK_MODE_BTN_ID} span.dark{
      display:block;
    }
    #${DARK_MODE_BTN_ID} span.light,.${DARK_MODE_CLASS_NAME} #${DARK_MODE_BTN_ID} span.dark{
      display:none;
    }

    .dynamic-page-btn{
      color: var(--text1);
      box-sizing: border-box;
      background-color: var(--bg1_float);
      border-radius: 6px;
      margin-top: 6px;
      width: 40px;
      height: 40px;
      text-align: center;
      padding: 0 4px;
      display: flex;
      flex-direction: column;
      justify-content: center;
      box-shadow: 0 0 10px rgba(0,0,0,.08);
      align-items: center;
      transition: all .2s;
      cursor: pointer;
      user-select: none;
    }
    .video-page-btn{
      cursor: pointer;
      background: #FFFFFF;
      background: var(--bg1_float);
      border: 1px solid #E3E5E7;
      border: 1px solid var(--line_regular);
      border-radius: 8px;
      box-sizing: border-box;
      padding: 6px;
      margin-bottom: 6px;
      color: #18191C;
      color: var(--text1);
      line-height: 14px;
      font-size: 12px;
      display: flex;
      flex-direction: column;
      align-items: center;
      width: 40px;
      user-select: none;
    }

`;
    // 其他用户动态页适配
    if (/^https:\/\/space\.bilibili\.com\/\d+\/dynamic$/.test(currentUrl)) {
      style.textContent += `
    .n .n-inner{
      background: var(--bg1);
      box-shadow: 0 0 0 1px var(--line_regular);
      color: var(--text1);
    }
    #page-dynamic .col-2 .section{
     background: var(--bg1);
    }
    .user-info .user-info-title .info-title{
     color: var(--text1) !important;
    }
    .n .n-data .n-data-v{
     color: var(--text1);
    }
    .g-search input{
       border: 1px solid var(--line_regular);
       background: var(--bg1);
    }
    body{
      height:auto;
      background-color:var(--bg3) !important;
    }
    :root.${DARK_MODE_CLASS_NAME} #page-dynamic .bili-dyn-item,
    :root.${DARK_MODE_CLASS_NAME} #page-dynamic .col-2 .section {
      border: 1px solid var(--line_regular);
    }
    :root.${DARK_MODE_CLASS_NAME} .user-info .user-info-title {
      border-bottom: 1px solid var(--line_regular);
    }


    `;
    }
    document.head.appendChild(style);
  }

  addDarkModeStyle();
  initDarkMode();
  registerMenuCommand();
  waitForMountDarkModeBtn();
})();