Claude 背景色控制器

调节 Claude 背景色

// ==UserScript==
// @name         Claude 背景色控制器
// @author       夏天的清晨
// @namespace    https://claude.ai
// @version      1.0.1
// @description  调节 Claude 背景色
// @match        https://claude.ai/*
// @license      MIT
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @run-at       document-start
// ==/UserScript==

(function () {
  "use strict";

  const DEF = {
    enabled: GM_getValue("enabled", true),
    bodyColor: GM_getValue("bodyColor", "#1e1e1e"),
    bodyAlpha: GM_getValue("bodyAlpha", 1),
    divColor: GM_getValue("divColor", "#ffffff"),
    divAlpha: GM_getValue("divAlpha", 1),
    hotkey: GM_getValue("hotkey", "Alt+B"),
  };

  function hexToRgba(hex, alpha) {
    const m = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex.trim());
    if (!m) return `rgba(30,30,30,${alpha})`;
    const r = parseInt(m[1], 16);
    const g = parseInt(m[2], 16);
    const b = parseInt(m[3], 16);
    return `rgba(${r}, ${g}, ${b}, ${Math.min(Math.max(alpha,0),1)})`;
  }

  let styleEl;
  function applyStyle() {
    if (styleEl) styleEl.remove();
    if (!DEF.enabled) return;

    styleEl = document.createElement("style");
    styleEl.id = "tm-body-bg-style";
    styleEl.textContent = `
      html, body {
        background: ${hexToRgba(DEF.bodyColor, DEF.bodyAlpha)} !important;
        background-color: ${hexToRgba(DEF.bodyColor, DEF.bodyAlpha)} !important;
      }

      /* 按钮保持白色 */
      div.flex.items-center.justify-between button[data-testid="chat-menu-trigger"] {
        background: #fff !important;
        background-color: #fff !important;
      }

      /* 特定 div 背景动态控制 */
      div.flex.w-full.items-center.justify-between.gap-4.pl-11.lg\\:pl-8.gap-6.pr-2.pr-3 {
        background: ${hexToRgba(DEF.divColor, DEF.divAlpha)} !important;
        background-color: ${hexToRgba(DEF.divColor, DEF.divAlpha)} !important;
      }
    `;
    (document.head || document.documentElement).appendChild(styleEl);
  }

  applyStyle();

  // === 菜单注册,只注册一次,不重复 ===
  GM_registerMenuCommand("切换背景控制启用/停用", () => {
    DEF.enabled = !DEF.enabled;
    GM_setValue("enabled", DEF.enabled);
    applyStyle();
    alert(`背景控制:${DEF.enabled ? "已启用" : "已停用"}`);
  });

  GM_registerMenuCommand("设置 Body 颜色", () => {
    const c = prompt(`输入 Body 颜色 HEX(当前: ${DEF.bodyColor})`, DEF.bodyColor);
    if (!c) return;
    DEF.bodyColor = c;
    GM_setValue("bodyColor", DEF.bodyColor);
    applyStyle();
  });

  GM_registerMenuCommand("设置 Body 透明度", () => {
    const a = parseFloat(prompt(`输入 Body 透明度 0~1(当前: ${DEF.bodyAlpha})`, DEF.bodyAlpha));
    if (Number.isNaN(a)) return;
    DEF.bodyAlpha = Math.min(Math.max(a,0),1);
    GM_setValue("bodyAlpha", DEF.bodyAlpha);
    applyStyle();
  });

  GM_registerMenuCommand("设置首部颜色", () => {
    const c = prompt(`输入特定 div 颜色 HEX(当前: ${DEF.divColor})`, DEF.divColor);
    if (!c) return;
    DEF.divColor = c;
    GM_setValue("divColor", DEF.divColor);
    applyStyle();
  });

  GM_registerMenuCommand("设置首部透明度", () => {
    const a = parseFloat(prompt(`输入特定 div 透明度 0~1(当前: ${DEF.divAlpha})`, DEF.divAlpha));
    if (Number.isNaN(a)) return;
    DEF.divAlpha = Math.min(Math.max(a,0),1);
    GM_setValue("divAlpha", DEF.divAlpha);
    applyStyle();
  });

  GM_registerMenuCommand("关于/帮助", () => {
    alert([
      "Claude 背景控制器",
      "— Body 背景可调颜色与透明度",
      "— 特定 div 背景可调颜色与透明度",
      "— Alt+B 切换启用/停用",
      "— 按钮固定白色"
    ].join("\n"));
  });

  // === 快捷键监听 ===
  function matchHotkey(evt, hotkeyStr) {
    const parts = hotkeyStr.toLowerCase().split("+").map(s => s.trim());
    const need = {
      altKey: parts.includes("alt"),
      ctrlKey: parts.includes("ctrl") || parts.includes("control"),
      shiftKey: parts.includes("shift"),
      metaKey: parts.includes("meta") || parts.includes("cmd") || parts.includes("command"),
      key: parts[parts.length-1]
    };
    const keyOk = evt.key && evt.key.toLowerCase() === need.key;
    return (
      keyOk &&
      (!!evt.altKey === need.altKey) &&
      (!!evt.ctrlKey === need.ctrlKey) &&
      (!!evt.shiftKey === need.shiftKey) &&
      (!!evt.metaKey === need.metaKey)
    );
  }

  window.addEventListener("keydown", e => {
    const t = e.target;
    if (t && (t.isContentEditable || /^(input|textarea|select)$/i.test(t.tagName))) return;
    if (matchHotkey(e, DEF.hotkey)) {
      e.preventDefault();
      DEF.enabled = !DEF.enabled;
      GM_setValue("enabled", DEF.enabled);
      applyStyle();
    }
  }, true);

  // === DOM 观察 ===
  const mo = new MutationObserver(() => {
    if (DEF.enabled && !document.getElementById("tm-body-bg-style")) {
      applyStyle();
    }
  });
  mo.observe(document.documentElement, { childList:true, subtree:true });
})();