Better Style

Apply Custom Themes for diep.io

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

Advertisement:

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

Advertisement:

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Better Style
// @namespace    http://tampermonkey.net/
// @version      20260219
// @description  Apply Custom Themes for diep.io
// @author       darkforce
// @match        https://mobile.diep.io/*
// @match        https://diep.io/*
// @icon         https://image2url.com/r2/default/images/1771394161131-f1ca2c6e-ead4-49d7-b884-6dfe8dd2a801.png
// @license      MIT
// @grant        none
// ==/UserScript==

(function () {
  "use strict";

  const CONFIG = {
    JS_COLOR_URL: "https://cdnjs.cloudflare.com/ajax/libs/jscolor/2.0.4/jscolor.min.js",
    BRAND_ICON: "https://image2url.com/r2/default/images/1771394161131-f1ca2c6e-ead4-49d7-b884-6dfe8dd2a801.png",
    ICONS: {
      SAVE_DARK: "https://image2url.com/r2/default/images/1771393483164-dca147c3-1b53-436f-83bd-1595441d059a.png",
      SAVE_LIGHT: "https://image2url.com/r2/default/images/1771393483164-dca147c3-1b53-436f-83bd-1595441d059a.png",
      IMPORT_DARK: "https://image2url.com/r2/default/images/1771393536379-02a075e0-8347-482a-972b-5ef12d2f01d3.png",
      IMPORT_LIGHT: "https://image2url.com/r2/default/images/1771430038800-a515edfa-bded-4434-a759-5e7d81577ee4.png",
      EXPORT_DARK: "https://image2url.com/r2/default/images/1771393507955-9bd65498-c59d-47a7-b61d-eb0158c9f367.png",
      EXPORT_LIGHT: "https://image2url.com/r2/default/images/1771430075200-068bd3c2-d62a-4fa3-bf3e-4e955a3c7c57.png",
      MARKET_DARK: "https://image2url.com/r2/default/images/1771393536379-02a075e0-8347-482a-972b-5ef12d2f01d3.png",
      MARKET_LIGHT: "https://image2url.com/r2/default/images/1771430038800-a515edfa-bded-4434-a759-5e7d81577ee4.png"
    },
    FONTS: {
      LIST: ["Ubuntu", "Protest Riot", "Inter", "Poppins", "Fira Code", "Playfair Display", "Caveat", "Comic Neue", "Cinzel", "Bangers", "Creepster", "Righteous", "Russo One", "Permanent Marker"],
      URL: "https://fonts.googleapis.com/css2?family=Bangers&family=Caveat&family=Cinzel&family=Comic+Neue&family=Creepster&family=Fira+Code&family=Inter:wght@400;700&family=Permanent+Marker&family=Playfair+Display&family=Poppins:wght@400;700&family=Protest+Riot&family=Righteous&family=Russo+One&display=swap"
    },
    KEYS: {
      STORAGE: "betterstyle_v3_settings",
      THEME: "betterstyle_Themes_v3_local",
      SELECTED_THEME: "betterstyle_selected_theme_id",
      MARKET: "diep_marketplace_themes"
    },
    MARKET_API: {
      ENDPOINT: "https://actual-wasp-57164.upstash.io",
      TOKEN: "Ad9MAAIncDI1MjJlMzFkNzVmMTk0YjBmYmE0YjMyNDdmMWJkMmNhOXAyNTcxNjQ"
    }
  };

  let localStorage;
  let nowSetting;
  let isLocal;
  let clone;
  let customThemes = [];
  let panelOn = false;

  jsInit();
  setTimeout(pluginInit, 2000);

  function setStorageObject(key, value) {
    localStorage.setItem(key, JSON.stringify(value));
  }

  function getStorageObject(key) {
    const value = localStorage.getItem(key);
    return value ? JSON.parse(value) : null;
  }

  function pluginSave() {
    const _storageObj = {
      nowSetting: clone(nowSetting),
    };
    setStorageObject(CONFIG.KEYS.STORAGE, _storageObj);
  }

  function jsInit() {
    clone = function (obj) {
      return JSON.parse(JSON.stringify(obj));
    };
    window.diepStyle = {};

    localStorage = window.localStorage;
    window.diepStyle.currentThemeId = localStorage.getItem(CONFIG.KEYS.SELECTED_THEME) || null;

    if (location.href.indexOf("file://") >= 0) {
      isLocal = true;
    }
    const storedThemes = getStorageObject(CONFIG.KEYS.THEME);
    if (storedThemes && Array.isArray(storedThemes)) {
      customThemes = storedThemes.map(t => t.id ? t : { ...t, id: Date.now() + Math.random().toString() });
    }
    const link = document.createElement('link');
    link.href = CONFIG.FONTS.URL;
    link.rel = "stylesheet";
    document.head.appendChild(link);
  }

  function escapeHtml(text) {
    if (!text) return "";
    return text
      .replace(/&/g, "&")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#039;");
  }

  function resizeImage(file, maxWidth, maxHeight, callback) {
    const img = new Image();
    const url = URL.createObjectURL(file);
    img.src = url;

    img.onload = () => {
      const canvas = document.createElement('canvas');
      let width = img.width;
      let height = img.height;

      if (width > height) {
        if (width > maxWidth) {
          height *= maxWidth / width;
          width = maxWidth;
        }
      } else {
        if (height > maxHeight) {
          width *= maxHeight / height;
          height = maxHeight;
        }
      }

      canvas.width = width;
      canvas.height = height;
      const ctx = canvas.getContext('2d');
      ctx.drawImage(img, 0, 0, width, height);

      const dataUrl = canvas.toDataURL('image/jpeg', 0.7);

      callback(dataUrl);
      URL.revokeObjectURL(url);
    };

    img.onerror = () => {
      URL.revokeObjectURL(url);
      console.error("Failed to load image for resizing");
    };
  }

  function pluginInit() {
    storageInit();
    keyListen();
    tempInit();
    styleInit();
    diepStyle.onColor = onColor;
    diepStyle.storageInit = storageInit;

    (function initFontHook() {
      const domStyle = document.createElement('style');
      domStyle.id = "diep-font-style-hook";
      document.head.appendChild(domStyle);

      function updateDOMFont(fontName) {
        if (!fontName) return;
        domStyle.innerHTML = `body, * { font-family: '${fontName}', Ubuntu, sans-serif !important; }`;
      }

      if (nowSetting && nowSetting.font) updateDOMFont(nowSetting.font);
      const originalFn = diepStyle.command.fn;
      diepStyle.command.fn = function(cmd, value) {
        originalFn(cmd, value);
        if (cmd === 'custom_font') {
          updateDOMFont(value);
        }
      };

      try {
        const fontDesc = Object.getOwnPropertyDescriptor(CanvasRenderingContext2D.prototype, "font");
        if (!fontDesc) return;

        const originalSetter = fontDesc.set;
        const originalGetter = fontDesc.get;

        Object.defineProperty(CanvasRenderingContext2D.prototype, "font", {
          set(value) {
            if (nowSetting && nowSetting.font) {
              value = value.replace(/Ubuntu|sans-serif/gi, nowSetting.font);
            }
            originalSetter.call(this, value);
          },
          get() {
            return originalGetter.call(this);
          },
          configurable: true
        });
      } catch (e) {
        console.error("Better Style: Font hook failed", e);
      }
    })();

    function storageInit(cmd) {
      let th = 50, netTH = 110;
      const colors = [
        { id: 2, name: "You FFA", color: "00b1de" },
        { id: 15, name: "Other FFA", color: "f14e54" },
        { id: 3, name: "Blue Team", color: "00b1de" },
        { id: 4, name: "Red Team", color: "f14e54" },
        { id: 5, name: "Purple Team", color: "bf7ff5" },
        { id: 6, name: "Green Team", color: "00e16e" },
        { id: 17, name: "Fallen Team", color: "c6c6c6" },
        { id: 12, name: "Arena Closer", color: "ffe869" },
        { id: 8, name: "Square", color: "ffe869" },
        { id: 7, name: "Green Square", color: "89ff69" },
        { id: 16, name: "Necro Square", color: "fcc376" },
        { id: 9, name: "Triangle", color: "fc7677" },
        { id: 10, name: "Pentagon", color: "768dfc" },
        { id: 18, name: "Hexagon", color: "32c5db" },
        { id: 11, name: "Crasher", color: "f177dd" },
        { id: 14, name: "Maze Wall", color: "bbbbbb" },
        { id: 1, name: "Turret", color: "999999" },
        { id: 0, name: "Smasher", color: "4f4f4f" },
        { id: th++, name: "All Bars", color: "000000", cmd: "ren_bar_background_color" },
        { id: th++, name: "Outline", color: "555555", cmd: "ren_stroke_solid_color" },
        { id: 13, name: "Leader Board", color: "64ff8c" },
        { id: th++, name: "Xp Bar", color: "ffde43", cmd: "ren_xp_bar_fill_color" },
        { id: th++, name: "Score Bar", color: "43ff91", cmd: "ren_score_bar_fill_color" },
        { id: th++, name: "Health Bar1", color: "85e37d", cmd: "ren_health_fill_color" },
        { id: th++, name: "Health Bar2", color: "555555", cmd: "ren_health_background_color" },
        { id: th++, name: "Grid Color", color: "000000", cmd: "ren_grid_color" },
        { id: th++, name: "Minimap 1", color: "CDCDCD", cmd: "ren_minimap_background_color" },
        { id: th++, name: "Minimap 2", color: "797979", cmd: "ren_minimap_border_color" },
        { id: th++, name: "Background 1", color: "CDCDCD", cmd: "ren_background_color" },
        { id: th++, name: "Background 2", color: "797979", cmd: "ren_border_color" },
        { id: netTH++, name: "UI Color1", color: "e69f6c", cmd: "ui_replace_colors" },
        { id: netTH++, name: "UI Color2", color: "ff73ff", cmd: "ui_replace_colors" },
        { id: netTH++, name: "UI Color3", color: "c980ff", cmd: "ui_replace_colors" },
        { id: netTH++, name: "UI Color4", color: "71b4ff", cmd: "ui_replace_colors" },
        { id: netTH++, name: "UI Color5", color: "ffed3f", cmd: "ui_replace_colors" },
        { id: netTH++, name: "UI Color6", color: "ff7979", cmd: "ui_replace_colors" },
        { id: netTH++, name: "UI Color7", color: "88ff41", cmd: "ui_replace_colors" },
        { id: netTH++, name: "UI Color8", color: "41ffff", cmd: "ui_replace_colors" },
      ];
      diepStyle.colorMap = new Map(
        colors.map(function (elem) {
          return [
            elem.id,
            { color: elem.color, cmd: elem.cmd || "no cmd" },
          ];
        })
      );
      diepStyle.uiColorMap = function (cmd) {
        const uiTH = nowSetting.colors.findIndex((elem) => elem.name == "UI Color1");
        let colorBunch = "";
        const arr = [];
        if (cmd == "0x") {
          for (let i = 0; i < 8; i++) {
            colorBunch = " 0x" + nowSetting.colors[uiTH + i].color + colorBunch;
          }
          return colorBunch;
        }
        if (cmd == "array") {
          for (let i = 0; i < 8; i++) {
            arr.push(nowSetting.colors[uiTH + i].color);
          }
          return arr;
        }
      };
      const renders = [
        { name: "Grid Alpha", value: 0.1, cmd: "grid_base_alpha" },
        { name: "Outline Intensity", value: 0.25, cmd: "stroke_soft_color_intensity" },
        { name: "Show Outline", value: false, cmd: "stroke_soft_color", reverse: true },
        { name: "Border Alpha", value: 0.1, cmd: "border_color_alpha" },
        { name: "UI Scale", value: 1, cmd: "ui_scale" },
        { name: "Clear UI", value: false, cmd: "ui", reverse: true },
        { name: "Show FPS", value: false, cmd: "fps" },
        { name: "Show Ping", value: false, cmd: "latency" },
        { name: "Show Health", value: false, cmd: "raw_health_values" },
        { name: "Hide Name", value: false, cmd: "names", reverse: true },
        { name: "Font Face", value: "Ubuntu", cmd: "custom_font", type: "select" }
      ];

      (function checkHasStorage() {
        const _localStorage = getStorageObject(CONFIG.KEYS.STORAGE);

        if (_localStorage && _localStorage.nowSetting) {
          nowSetting = _localStorage.nowSetting;
        }

        if (!nowSetting || cmd == "reset") {
          nowSetting = getBlankSetting();
        }

        const plain = getBlankSetting();
        plain.renders.forEach((elem) => {
          if (!nowSetting.renders.find((now) => elem.cmd === now.cmd)) {
            nowSetting.renders.push(elem);
          }
        });
        plain.colors.forEach((elem) => {
          if (!nowSetting.colors.find((now) => (elem.cmd && elem.cmd === now.cmd) || (elem.id !== undefined && elem.id === now.id))) {
            nowSetting.colors.push(elem);
          }
        });
        if (!nowSetting.font) nowSetting.font = "Ubuntu";
      })();

      (function command() {
        diepStyle.command = {};
        renders.forEach((elem) => {
          diepStyle.command[elem.cmd] = {};
          if (elem.reverse) diepStyle.command[elem.cmd].reverse = true;
        });
        diepStyle.command.fn = function (cmd, value) {
          nowSetting.renders = nowSetting.renders.map((elem) => {
            if (elem.cmd == cmd) elem.value = value;
            return elem;
          });
          if (cmd === "custom_font") {
            nowSetting.font = value;
            return;
          }
          if (diepStyle.command[cmd].reverse) value = !value;
          if (window.input) {
            window.input.set_convar("ren_" + cmd, value);
          }
        };
      })();

      function getBlankSetting() {
        return {
          version: 3.1,
          lock: false,
          colors: clone(colors),
          renders: clone(renders),
          uiMode: 'light',
          font: 'Ubuntu'
        };
      }

      diepStyle.getBlankSetting = getBlankSetting;

      pluginSave();
    }

    function keyListen() {
      let lastEscTime = 0;
      const performToggle = () => {
        panelOn = !panelOn;
        togglePanel(panelOn);
        if (!panelOn) {
          pluginSave();
        }
      };

      document.addEventListener("keyup", function (evt) {
        if (evt.key === "Escape") {
          const now = Date.now();
          if (now - lastEscTime < 500) {
            performToggle();
            lastEscTime = 0;
          } else {
            lastEscTime = now;
          }
        }
      });

      let tapCount = 0;
      let tapTimer = null;
      const handleAreaTrigger = (e) => {
        const x = e.clientX || (e.touches && e.touches[0] ? e.touches[0].clientX : 0);
        const y = e.clientY || (e.touches && e.touches[0] ? e.touches[0].clientY : 0);
        const screenW = window.innerWidth;
        const screenH = window.innerHeight;

        if (x < screenW * 0.15 && y > screenH * 0.85) {
          tapCount++;
          clearTimeout(tapTimer);
          if (tapCount === 4) {
            tapCount = 0;
            performToggle();
          }
          tapTimer = setTimeout(() => { tapCount = 0; }, 600);
        } else {
          tapCount = 0;
        }
      };
      document.addEventListener("mousedown", handleAreaTrigger);
      document.addEventListener("touchstart", handleAreaTrigger, { passive: true });
    }

    function tempInit() {
      diepStyle.exportJSON = exportJSON;
      diepStyle.importJSON = importJSON;

      init1();
      loadColor();
      setTimeout(diepStyle.resetRender, 1500);
      diepStyle.resetColor = loadColor;

      function init1() {
        diepStyle.resetRender = resetRender;
        const title = `
            <div class="header-container">
                <div class="title">
                    <img src="${CONFIG.BRAND_ICON}" alt="logo" style="height: 24px; vertical-align: middle; margin-right: 8px;">
                    Better Style
                </div>
                <div class="toggle-container">
                   <label class="switch">
                      <input type="checkbox" id="darkModeToggle">
                      <span class="slider round"></span>
                   </label>
                   <span class="toggle-label">Dark UI</span>
                </div>
            </div>
            <div class="subtitle">Press Esc twice to toggle</div>`;
        const colorPlane = function (id) {
          return `{position:'left',width:300, height:200,zIndex:10000,onFineChange:'diepStyle.onColor(${id},this)'}`;
        };

        function updateOverwriteBtn() {
           const btn = document.getElementById('overwriteThemeBtn');
           if(btn) {
                const theme = getCombinedThemes().find(t => t.id === diepStyle.currentThemeId);
                const isReadOnly = theme && theme.readOnly;
                btn.disabled = !diepStyle.currentThemeId || !theme || isReadOnly;
           }
        }

        function getCombinedThemes() {
             const defaultObj = {
                 id: "default_theme_readonly",
                 name: "Default",
                 data: diepStyle.exportJSON("object", diepStyle.getBlankSetting()),
                 readOnly: true
             };
             return [defaultObj, ...customThemes];
        }

        function customAlert(msg) {
             const m = document.getElementById('ds-msg-modal');
             const t = document.getElementById('ds-msg-text');
             const ok = document.getElementById('ds-msg-ok');
             const cancel = document.getElementById('ds-msg-cancel');
             const title = document.getElementById('ds-msg-title');

             title.innerText = "Notice";
             t.innerText = msg;
             m.classList.remove('hide');
             cancel.style.display = 'none';

             ok.onclick = function() {
                 m.classList.add('hide');
             }
        }

        function customConfirm(msg, onYes) {
             const m = document.getElementById('ds-msg-modal');
             const t = document.getElementById('ds-msg-text');
             const ok = document.getElementById('ds-msg-ok');
             const cancel = document.getElementById('ds-msg-cancel');
             const title = document.getElementById('ds-msg-title');

             title.innerText = "Confirm";
             t.innerText = msg;
             m.classList.remove('hide');
             cancel.style.display = 'inline-block';

             ok.onclick = function() {
                 m.classList.add('hide');
                 if(onYes) onYes();
             }
             cancel.onclick = function() {
                 m.classList.add('hide');
             }
        }

        function resetRender(cmd) {
          document.querySelectorAll("#styleSetting .render").forEach(function (elem) {
              elem.outerHTML = ``;
          });
          const it = document.querySelector(".renderBegin");
          if(!it) return;

          it.insertAdjacentHTML("afterend", getRenderBody());

          nowSetting.renders.forEach(function (elem) {
            diepStyle.command.fn(elem.cmd, elem.value);
          });
          bindRenderListeners();

          const toggle = document.getElementById('darkModeToggle');
          if (nowSetting.uiMode === 'dark') {
            document.getElementById('styleSetting').classList.add('dark-mode');
            if (toggle) toggle.checked = true;
          } else {
            document.getElementById('styleSetting').classList.remove('dark-mode');
            if (toggle) toggle.checked = false;
          }

          updateIcons(nowSetting.uiMode === 'dark');
          updateOverwriteBtn();
        }

        const bodyTheme = getThemeBody();
        const bodyRender = getRenderBody();
        const bodyColor = getColorBody();

        function getThemeBody() {
          const allThemes = getCombinedThemes();
          const isDark = nowSetting.uiMode === 'dark';
          const iconSrc = isDark ? CONFIG.ICONS.SAVE_DARK : CONFIG.ICONS.SAVE_LIGHT;
          let themeListHTML = allThemes.map((t) => {
            const isSelected = diepStyle.currentThemeId === t.id;
            const activeClass = isSelected ? "selected-theme" : "";
            const deleteBtn = t.readOnly
                ? `<button class="delete-theme-btn" disabled style="opacity:0.3; cursor:default;">×</button>`
                : `<button class="delete-theme-btn" data-id="${t.id}">×</button>`;

            return `
                <div class="theme-card ${activeClass}" data-id="${t.id}">
                    <div class="theme-name" title="${escapeHtml(t.name)}">${escapeHtml(t.name)}</div>
                     <div class="theme-actions">
                        <button class="load-theme-btn" data-id="${t.id}" ${isSelected ? 'disabled' : ''}>${isSelected ? 'Active' : 'Select'}</button>
                        ${deleteBtn}
                    </div>
                </div>`;
          }).join('');
          const html = `
                <div class="themeBody">
                    <div class="section-title">Local Themes</div>
                    <div class="theme-controls">
                        <input type="text" id="newThemeName" placeholder="Theme Name">
                        <button id="saveThemeBtn" title="Save as new theme"><img class="btn-icon icon-save" src="${iconSrc}" alt="">Save New</button>
                        <button id="overwriteThemeBtn" title="Overwrite currently loaded theme" disabled>Overwrite</button>
                    </div>
                    <div class="theme-list" id="customThemeList">
                        ${themeListHTML || '<div class="no-themes">No themes available.</div>'}
                    </div>
                </div>
            `;
          return html;
        }

        function getRenderBody() {
          const renders = nowSetting.renders;
          let html = ``;

          renders.forEach((r) => {
              if (r.type === 'select' && r.cmd === 'custom_font') {
                  let options = CONFIG.FONTS.LIST.map(f => `<option value="${f}" ${r.value === f ? 'selected' : ''}>${f}</option>`).join('');
                  html += `<div class="row render">
                    <div class="cell label">Font Face</div>
                    <div class="cell input-area">
                        <select name="${r.cmd}" class="font-select">
                            ${options}
                        </select>
                    </div>
                  </div>`;
              } else if (r.cmd.includes('alpha') || r.cmd.includes('intensity') || r.cmd.includes('scale')) {
                   let max = r.cmd.includes('alpha') || r.cmd === 'ui_scale' ? 200 : 100;
                   if (r.cmd === 'stroke_soft_color_intensity') max = 100;

                   html += `<div class="row render">
                    <div class="cell label">${r.name}</div>
                    <div class="cell value-display"><span class="${r.cmd}_value">${r.value}</span></div>
                    <div class="cell input-area">
                        <input type="range" name="${r.cmd}" value="${r.value * 100}" max="${max}">
                    </div>
                   </div>`;
              } else {
                  html += `<div class="row render">
                    <div class="cell label">${r.name}</div>
                    <div class="cell input-area check-area">
                       <label class="switch">
                           <input type="checkbox" name="${r.cmd}" ${r.value ? "checked" : ""}>
                            <span class="slider round"></span>
                        </label>
                    </div>
                 </div>`;
              }
          });
          return html;
        }

        function getColorBody() {
          let it = `<div class="section-title">Colors</div><div class="color-grid">`;
          nowSetting.colors.forEach(function (elem, th) {
            const id = elem.id;
            it += `
                <div class="color-item colorBlock${th}">
                    <div class="color-label">${elem.name}</div>
                    <input class="jscolor ${colorPlane(`${id}`)}">
                </div>
            `;
          });
          it += `</div>`;
          return it;
        }

        const isDark = nowSetting.uiMode === 'dark';
        const importIcon = isDark ? CONFIG.ICONS.IMPORT_DARK : CONFIG.ICONS.IMPORT_LIGHT;
        const exportIcon = isDark ? CONFIG.ICONS.EXPORT_DARK : CONFIG.ICONS.EXPORT_LIGHT;
        const allBody = `
            <div class="pluginBody">
                ${title}
                <hr>
                ${bodyTheme}
                <hr>
                <div class="section-title renderBegin">Render Settings</div>
                ${bodyRender}
                <hr>
                ${bodyColor}
                <br>
            </div>
            `;
        const footer = `
            <div class="footer">
                <div class="action-btns">
                    <button class="import action-btn"><img class="btn-icon icon-import" src="${importIcon}" alt="">Import JSON</button>
                    <button class="export action-btn"><img class="btn-icon icon-export" src="${exportIcon}" alt="">Export JSON</button>
                    <button class="marketplace-btn action-btn" style="background:#768dfc; color:white; border:none;">Marketplace</button>
                    <button class="lock-btn action-btn">Lock</button>
                    <button class="reset-btn action-btn" style="background:#ff7979; color:white; border:none;">Reset</button>
                </div>
            </div>
        `;
        const modalHTML = `
            <div id="ds-modal" class="ds-modal hide">
                <div class="ds-modal-content">
                    <span class="ds-close">&times;</span>
                    <h3 id="ds-modal-title">Import/Export</h3>
                    <p id="ds-modal-desc"></p>
                    <textarea id="ds-modal-text"></textarea>
                    <div class="ds-modal-footer">
                        <button id="ds-modal-action">Import</button>
                        <button id="ds-modal-copy">Copy to Clipboard</button>
                    </div>
                </div>
            </div>
        `;
        const msgModalHTML = `
            <div id="ds-msg-modal" class="ds-modal hide">
                <div class="ds-modal-content" style="max-width: 400px; text-align:center;">
                    <h3 id="ds-msg-title">Alert</h3>
                    <p id="ds-msg-text" style="font-size:1.1em; margin: 20px 0;"></p>
                    <div class="ds-modal-footer" style="display:flex; justify-content:center; gap:10px;">
                        <button id="ds-msg-cancel" style="background:#ff4757;">Cancel</button>
                        <button id="ds-msg-ok">OK</button>
                    </div>
                </div>
            </div>
        `;
        const marketModalHTML = `
            <div id="ds-market-modal" class="ds-modal hide">
                <div class="ds-modal-content" style="max-width: 700px; height: 600px; display:flex; flex-direction:column;">
                    <div style="display:flex; justify-content:space-between; align-items:center;">
                         <h3 style="margin-top:0;">Theme Marketplace</h3>
                         <span class="ds-close-market" style="font-size:28px; cursor:pointer;">&times;</span>
                    </div>
                    <div style="display:flex; gap:10px; margin-bottom:10px;">
                        <button id="refreshMarketBtn" class="action-btn">Refresh List</button>
                        <button id="openPublishModalBtn" class="action-btn" style="background:#00e16e; color:white; border:none;">Publish Theme</button>
                    </div>
                    <div id="market-list" style="flex:1; overflow-y:auto; border:1px solid var(--border-color); border-radius:4px; padding:10px; background: rgba(0,0,0,0.05);">
                         Loading...
                    </div>
                </div>
            </div>
        `;
        const publishModalHTML = `
            <div id="ds-publish-modal" class="ds-modal hide">
                <div class="ds-modal-content">
                    <span class="ds-close-publish">&times;</span>
                    <h3>Publish Theme</h3>
                    <div class="form-group">
                         <label>Author Name:</label>
                        <input type="text" id="publishAuthor" placeholder="Anonymous">
                    </div>
                    <div class="form-group">
                        <label>Theme Name:</label>
                        <input type="text" id="publishName" placeholder="My Cool Theme">
                    </div>
                    <div class="form-group">
                         <label>Preview Image (Canvas Optimized):</label>
                         <input type="file" id="publishImageInput" accept="image/png, image/jpeg">
                         <div id="imagePreviewContainer" style="margin-top:5px; border:1px solid #ccc; min-height:50px; display:flex; align-items:center; justify-content:center; background:#eee;">
                             <span style="color:#888;">No image selected</span>
                             <img id="imagePreviewImg" style="max-width:100%; max-height:150px; display:none;">
                         </div>
                    </div>
                    <div class="ds-modal-footer" style="margin-top:15px;">
                         <button id="confirmPublishBtn" style="background:#00e16e;">Publish Now</button>
                    </div>
                </div>
            </div>
        `;

        const temp = `<div id="styleSetting">${allBody} ${footer} </div> ${modalHTML} ${marketModalHTML} ${publishModalHTML} ${msgModalHTML}`;
        document.body.insertAdjacentHTML("beforeend", temp);
        addScript(CONFIG.JS_COLOR_URL);

        staticListenerInit();
        togglePanel(false);

        function updateIcons(isDark) {
            document.querySelectorAll('.icon-save').forEach(img => img.src = isDark ? CONFIG.ICONS.SAVE_DARK : CONFIG.ICONS.SAVE_LIGHT);
            document.querySelectorAll('.icon-import').forEach(img => img.src = isDark ? CONFIG.ICONS.IMPORT_DARK : CONFIG.ICONS.IMPORT_LIGHT);
            document.querySelectorAll('.icon-export').forEach(img => img.src = isDark ? CONFIG.ICONS.EXPORT_DARK : CONFIG.ICONS.EXPORT_LIGHT);
            document.querySelectorAll('.icon-market').forEach(img => img.src = isDark ? CONFIG.ICONS.MARKET_DARK : CONFIG.ICONS.MARKET_LIGHT);
        }

        function staticListenerInit() {
          document.getElementById('darkModeToggle').addEventListener('change', function (e) {
            const isDark = e.target.checked;
            if (isDark) {
              document.getElementById('styleSetting').classList.add('dark-mode');
              nowSetting.uiMode = 'dark';
            } else {
              document.getElementById('styleSetting').classList.remove('dark-mode');
              nowSetting.uiMode = 'light';
            }
            updateIcons(isDark);
            pluginSave();
          });
          document.querySelector('.marketplace-btn').addEventListener('click', function () {
            document.getElementById('ds-market-modal').classList.remove('hide');
          });
          document.querySelector('.ds-close-market').addEventListener('click', function () {
            document.getElementById('ds-market-modal').classList.add('hide');
          });
          document.getElementById('refreshMarketBtn').addEventListener('click', loadMarketplace);
          document.getElementById('openPublishModalBtn').addEventListener('click', function () {
            document.getElementById('ds-market-modal').classList.add('hide');
            document.getElementById('ds-publish-modal').classList.remove('hide');
            document.getElementById('publishName').value = '';
            document.getElementById('publishImageInput').value = '';
            document.getElementById('imagePreviewImg').style.display = 'none';
            document.getElementById('imagePreviewContainer').querySelector('span').style.display = 'block';
          });
          document.querySelector('.ds-close-publish').addEventListener('click', function () {
            document.getElementById('ds-publish-modal').classList.add('hide');
          });
          const fileInput = document.getElementById('publishImageInput');
          const previewImg = document.getElementById('imagePreviewImg');
          const previewPlaceholder = document.getElementById('imagePreviewContainer').querySelector('span');
          let currentBase64Image = null;
          fileInput.addEventListener('change', function (e) {
            const file = e.target.files[0];
            if (!file) return;

            const tempUrl = URL.createObjectURL(file);
            previewImg.src = tempUrl;
            previewImg.style.display = 'block';
            previewPlaceholder.style.display = 'none';

            resizeImage(file, 200, 150, (smallDataUrl) => {
                 currentBase64Image = smallDataUrl;
            });
          });
          document.getElementById('confirmPublishBtn').addEventListener('click', function () {
            const author = document.getElementById('publishAuthor').value || "Anonymous";
            const themeName = document.getElementById('publishName').value;
            if (!themeName) { customAlert("Please enter a theme name."); return; }

            publishTheme(themeName, author, currentBase64Image);
          });
          const modal = document.getElementById("ds-modal");
          const modalText = document.getElementById("ds-modal-text");
          const modalTitle = document.getElementById("ds-modal-title");
          const modalDesc = document.getElementById("ds-modal-desc");
          const importBtn = document.getElementById("ds-modal-action");
          const copyBtn = document.getElementById("ds-modal-copy");
          const closeSpan = document.querySelector(".ds-close");

          closeSpan.onclick = () => modal.classList.add("hide");
          document.querySelector("button.import").addEventListener("click", () => {
            modal.classList.remove("hide");
            modalTitle.innerText = "Import JSON";
            modalDesc.innerText = "Paste theme JSON code here to apply it immediately:";
            modalText.value = "";
            importBtn.style.display = "inline-block";
            copyBtn.style.display = "none";

            importBtn.onclick = () => {
                diepStyle.importJSON(modalText.value);
                modal.classList.add("hide");

                diepStyle.currentThemeId = null;
                localStorage.removeItem(CONFIG.KEYS.SELECTED_THEME);

                refreshThemeListUI();
            };
          });
          document.querySelector("button.export").addEventListener("click", () => {
            modal.classList.remove("hide");
            modalTitle.innerText = "Export JSON";
            modalDesc.innerText = "Copy this code to share your current settings:";
            modalText.value = diepStyle.exportJSON("string");
            importBtn.style.display = "none";
            copyBtn.style.display = "inline-block";

            copyBtn.onclick = () => {
              modalText.select();
              document.execCommand("copy");
              copyBtn.innerText = "Copied!";
              setTimeout(() => copyBtn.innerText = "Copy to Clipboard", 1500);
            };
          });
          document.querySelector(".lock-btn").addEventListener("click", function () {
            nowSetting.lock = !nowSetting.lock;
            updateLockState();
          });
          const resetBtn = document.querySelector(".reset-btn");
          resetBtn.addEventListener("click", function (e) {
            if (e.target.innerHTML != "Confirm Reset") {
              e.target.innerHTML = "Confirm Reset";
            } else {
              diepStyle.storageInit("reset");
              diepStyle.resetColor();
              diepStyle.resetRender("reset");
              diepStyle.currentThemeId = "default_theme_readonly";
              localStorage.setItem(CONFIG.KEYS.SELECTED_THEME, diepStyle.currentThemeId);
              refreshThemeListUI();
              e.target.innerHTML = "Reset";
            }
          });
          resetBtn.addEventListener("mouseleave", e => e.target.innerHTML = "Reset");
          bindThemeActions();

          loadMarketplace();
        }

        function bindThemeActions() {
          const saveBtn = document.getElementById('saveThemeBtn');
          if(saveBtn) {
              saveBtn.onclick = function () {
                const name = document.getElementById('newThemeName').value;
                if (!name) { customAlert("Please enter a name for the new theme."); return; }
                const jsonObj = diepStyle.exportJSON("object");
                saveLocalTheme(name, jsonObj);
              };
          }
          const overwriteBtn = document.getElementById('overwriteThemeBtn');
          if(overwriteBtn) {
              overwriteBtn.onclick = function () {
                if (!diepStyle.currentThemeId) return;
                if(diepStyle.currentThemeId === 'default_theme_readonly') {
                    customAlert("Cannot overwrite the Default theme.");
                    return;
                }
                const currentTheme = customThemes.find(t => t.id === diepStyle.currentThemeId);
                if (currentTheme) {
                     customConfirm(`Overwrite theme "${currentTheme.name}" with current settings?`, function() {
                          const jsonObj = diepStyle.exportJSON("object");
                          saveLocalTheme(currentTheme.name, jsonObj, diepStyle.currentThemeId);
                     });
                }
              };
          }
          const themeList = document.querySelector('.theme-list');
          if(themeList) {
              themeList.onclick = function (e) {
                if (e.target.classList.contains('load-theme-btn')) {
                  const id = e.target.getAttribute('data-id');
                  const theme = getCombinedThemes().find(t => t.id == id);
                  if (theme) {
                    diepStyle.importJSON(theme.data);
                    diepStyle.currentThemeId = id;
                    localStorage.setItem(CONFIG.KEYS.SELECTED_THEME, id);
                    refreshThemeListUI();
                  }
                }
                if (e.target.classList.contains('delete-theme-btn')) {
                  const id = e.target.getAttribute('data-id');
                  if (id === "default_theme_readonly") return;

                  const idx = customThemes.findIndex(t => t.id == id);
                  if (idx !== -1) {
                      customConfirm("Are you sure you want to delete this theme?", function() {
                            customThemes.splice(idx, 1);
                            setStorageObject(CONFIG.KEYS.THEME, customThemes);
                            if (diepStyle.currentThemeId == id) {
                              diepStyle.currentThemeId = null;
                              localStorage.removeItem(CONFIG.KEYS.SELECTED_THEME);
                            }
                            refreshThemeListUI();
                      });
                  }
                }
              };
          }
        }

        function loadMarketplace() {
          const list = document.getElementById('market-list');
          list.innerHTML = "Loading Themes from Marketplace...";

          fetch(`${CONFIG.MARKET_API.ENDPOINT}/get/${CONFIG.KEYS.MARKET}`, {
            method: 'GET',
            headers: {
              'Authorization': `Bearer ${CONFIG.MARKET_API.TOKEN}`
            }
          })
            .then(response => response.json())
            .then(data => {
              let themes = [];
              if (data.result) {
                try {
                  themes = JSON.parse(data.result);
                } catch (e) {
                  console.error("Failed to parse Redis result:", e);
                  list.innerHTML = "Error parsing marketplace data.";
                  return;
                }
              }

              if (!Array.isArray(themes)) { themes = []; }

              if (themes.length === 0) {
                list.innerHTML = "No themes found in the marketplace. Be the first to publish!";
                return;
              }

              list.innerHTML = "";
              themes.slice().reverse().forEach((t, index) => {
                const card = document.createElement('div');
                card.className = "theme-card market-card";
                card.style.padding = "10px";
                card.style.marginBottom = "8px";
                card.style.background = "var(--modal-bg)";
                let imgHTML = '';
                if (t.previewImage) {
                  imgHTML = `<img src="${t.previewImage}" style="width:60px; height:40px; object-fit:cover; border-radius:4px; border:1px solid var(--border-color); margin-right:10px;">`;
                } else {
                  imgHTML = `<div style="width:60px; height:40px; background:#eee; border-radius:4px; margin-right:10px; display:flex; align-items:center; justify-content:center; font-size:9px; color:#999;">No Preview</div>`
                }

                const isDark = nowSetting.uiMode === 'dark';
                const iconSrc = isDark ? CONFIG.ICONS.SAVE_DARK : CONFIG.ICONS.SAVE_LIGHT;

                card.innerHTML = `
                          <div style="display:flex; flex:1; align-items:center;">
                              ${imgHTML}
                              <div>
                                  <div style="font-weight:bold; font-size:1.1em;">${escapeHtml(t.name)}</div>
                                  <div style="font-size:0.85em; opacity:0.7;">by ${escapeHtml(t.author)}</div>
                              </div>
                          </div>
                          <button class="load-market-btn action-btn" style="background:var(--accent-color); color:white; border:none; margin-left:10px;">
                             <img class="btn-icon icon-save" src="${iconSrc}" alt=""> Save to Local
                          </button>
                      `;
                card.querySelector('.load-market-btn').addEventListener('click', () => {
                  const saveName = prompt("Enter a name to save this theme locally:", t.name);
                  if (saveName) {
                    saveLocalTheme(saveName, t.data);
                    document.getElementById('ds-market-modal').classList.add('hide');
                    customAlert(`"${saveName}" saved to your local themes.`);
                  }
                });
                list.appendChild(card);
              });
            })
            .catch(err => {
              console.error(err);
              list.innerHTML = `<span style="color:red;">Error loading marketplace: ${err.message}</span>`;
            });
        }

        function publishTheme(name, author, base64Img) {
          const btn = document.getElementById('confirmPublishBtn');
          const originalText = btn.innerText;

          btn.innerText = "Publishing...";
          btn.disabled = true;
          btn.style.opacity = "0.5";
          btn.style.cursor = "not-allowed";
          fetch(`${CONFIG.MARKET_API.ENDPOINT}/get/${CONFIG.KEYS.MARKET}`, {
            method: 'GET',
            headers: { 'Authorization': `Bearer ${CONFIG.MARKET_API.TOKEN}` }
          })
            .then(res => res.json())
            .then(data => {
              let currentThemes = [];
              if (data.result) {
                try { currentThemes = JSON.parse(data.result); } catch (e) { }
              }

              if (!Array.isArray(currentThemes)) currentThemes = [];
              const newTheme = {
                name: name,
                author: author,
                date: new Date().toISOString(),
                data: diepStyle.exportJSON("object"),
                previewImage: base64Img
              };
              currentThemes.push(newTheme);

              return fetch(`${CONFIG.MARKET_API.ENDPOINT}`, {
                method: 'POST',
                headers: {
                  'Authorization': `Bearer ${CONFIG.MARKET_API.TOKEN}`
                },
                body: JSON.stringify(["SET", CONFIG.KEYS.MARKET, JSON.stringify(currentThemes)])
              });
            })
            .then(res => res.json())
            .then(result => {
              if (result.error) throw new Error(result.error);
              customAlert("Theme published successfully!");
              document.getElementById('ds-publish-modal').classList.add('hide');
              document.getElementById('ds-market-modal').classList.remove('hide');
              loadMarketplace();
            })
            .catch(err => {
              customAlert("Failed to publish: " + err.message);
            })
            .finally(() => {
              btn.innerText = originalText;
              btn.disabled = false;
              btn.style.opacity = "1";
              btn.style.cursor = "pointer";
            });
        }

        function refreshThemeListUI() {
          const list = document.querySelector('.themeBody');
          if(!list) return;
          list.outerHTML = getThemeBody();
          bindThemeActions();
          updateOverwriteBtn();
        }

        function saveLocalTheme(name, themeDataObj, existingId = null) {
          let targetId = existingId;
          if (existingId) {
            const idx = customThemes.findIndex(t => t.id == existingId);
            if (idx !== -1) {
              customThemes[idx].name = name;
              customThemes[idx].data = themeDataObj;
            }
          } else {
            targetId = Date.now().toString();
            customThemes.push({
              id: targetId,
              name: name,
              data: themeDataObj
            });
          }
          setStorageObject(CONFIG.KEYS.THEME, customThemes);

          diepStyle.currentThemeId = targetId;
          localStorage.setItem(CONFIG.KEYS.SELECTED_THEME, targetId);

          refreshThemeListUI();
        }

        function updateLockState() {
          const lockBtn = document.querySelector(".lock-btn");
          const resetBtn = document.querySelector(".reset-btn");
          if (nowSetting.lock) {
            document.querySelector(".pluginBody").classList.add("locked-ui");
            lockBtn.innerHTML = "Locked";
            resetBtn.disabled = true;
          } else {
            document.querySelector(".pluginBody").classList.remove("locked-ui");
            lockBtn.innerHTML = "Lock";
            resetBtn.disabled = false;
          }
        }
        updateLockState();
        function bindRenderListeners() {
            const bindSlider = (name, selector, calc) => {
              const el = document.querySelector(`input[name="${name}"]`);
              if (!el) return;
              const updateFill = (element) => {
                const val = element.value;
                const min = element.min ? element.min : 0;
                const max = element.max ? element.max : 100;
                const percentage = ((val - min) * 100) / (max - min);
                element.style.backgroundSize = percentage + "% 100%";
              };
              updateFill(el);
              el.addEventListener("input", function (e) {
                const value = calc(e.target.value);
                document.querySelector(selector).innerHTML = value;
                updateFill(e.target);
                diepStyle.command.fn(name, value);
              });
            };

            bindSlider("grid_base_alpha", ".grid_base_alpha_value", v => (v - (v % 2)) / 100);
            bindSlider("stroke_soft_color_intensity", ".stroke_soft_color_intensity_value", v => (v - (v % 5)) / 100);
            bindSlider("border_color_alpha", ".border_color_alpha_value", v => (v - (v % 2)) / 100);
            bindSlider("ui_scale", ".ui_scale_value", v => (v - (v % 2)) / 100);
            nowSetting.renders.forEach(r => {
                if (r.type !== 'select' && !r.cmd.includes('alpha') && !r.cmd.includes('intensity') && !r.cmd.includes('scale')) {
                    const el = document.querySelector(`input[name="${r.cmd}"]`);
                    if (el) el.addEventListener("change", e => diepStyle.command.fn(r.cmd, e.target.checked));
                }
             });

            const fontSel = document.querySelector('select[name="custom_font"]');
            if (fontSel) {
                fontSel.addEventListener("change", function(e) {
                    diepStyle.command.fn("custom_font", e.target.value);
                });
            }
        }
      }

      function loadColor() {
        nowSetting.colors.some(function (elem, th) {
          const target = document.querySelector(`.colorBlock${th}`);
          if (!target || !target.querySelector("input").jscolor) {
            setTimeout(loadColor, 500);
            return true;
          }
          onColor(elem.id, elem.color);
          target.querySelector("input").jscolor.fromString(elem.color);
        });
      }

      function exportJSON(mode = "string", sourceSettings = null) {
        const settingToUse = sourceSettings || nowSetting;
        const array = [];
        settingToUse.colors.forEach(function (elem) {
          if (elem.id && elem.id < 50)
            array.push({ id: elem.id, value: elem.color, });
          if (elem.id && elem.id >= 50 && elem.id < 100)
            array.push({ cmd: elem.cmd, value: elem.color, });
        });
        if(sourceSettings) {
             const uiTH = settingToUse.colors.findIndex(elem => elem.name == "UI Color1");
             const arr = [];
             for (let i = 0; i < 8; i++) {
                 arr.push(settingToUse.colors[uiTH + i].color);
             }
             array.push({
                cmd: "ui_replace_colors",
                value: arr,
             });
        } else {
             array.push({
                cmd: "ui_replace_colors",
                value: diepStyle.uiColorMap("array"),
             });
        }

        settingToUse.renders.forEach(function (elem) {
          array.push({ cmd: elem.cmd, value: elem.value });
        });
        if (!array.find(x => x.cmd === 'custom_font')) {
            array.push({ cmd: 'custom_font', value: settingToUse.font });
        }

        if (mode === "object") return array;
        return JSON.stringify(array);
      }

      function importJSON(jsonObj) {
        if (!jsonObj) { customAlert("Invalid data."); return; }
        let gotArr = jsonObj;
        if (typeof gotArr === 'string') {
          try {
            gotArr = JSON.parse(gotArr);
          } catch (e) { customAlert("Invalid JSON string"); return; }
        }

        gotArr.forEach(function (elem) {
          nowSetting.colors = nowSetting.colors.map(function (now) {
            if (elem.id && now.id == elem.id) now.color = elem.value;
            if (!elem.id && elem.cmd && now.cmd == elem.cmd)
              now.color = elem.value;
             return now;
          });
          nowSetting.renders = nowSetting.renders.map(function (now) {
            if (elem.cmd && now.cmd == elem.cmd) now.value = elem.value;
            return now;
          });
          if (elem.cmd == "ui_replace_colors") {
             const uiTH = nowSetting.colors.findIndex(
              (elem) => elem.name == "UI Color1"
            );
            for (let i = 0; i < 8; i++) {
              nowSetting.colors[uiTH + i].color = elem.value[i];
            }
          }
          if (elem.cmd === "custom_font") {
              nowSetting.font = elem.value;
              const rInd = nowSetting.renders.findIndex(r => r.cmd === 'custom_font');
              if(rInd >= 0) nowSetting.renders[rInd].value = elem.value;
          }
        });
        diepStyle.resetColor();
        diepStyle.resetRender();
        pluginSave();
      }
    }

    function onColor(id, e) {
      const target = id;
      const color = e.toString();
      if (window.input) {
        if (id >= 0 && id < 50) {
          input.execute(`net_replace_color ${target} 0x${color}`);
        } else if (id >= 50 && id < 100) {
          const cmd = diepStyle.colorMap.get(id).cmd;
          input.set_convar(cmd, `0x${color}`);
        } else {
          input.execute("ui_replace_colors" + diepStyle.uiColorMap("0x"));
        }
      }
      nowSetting.colors = nowSetting.colors.map(function (elem) {
        if (elem.id === id) elem.color = color;
        return elem;
      });
    }

    function styleInit() {
      addGlobalStyle(`
        :root {
            --bg-color: rgba(245, 245, 245, 0.98);
            --text-color: #333;
            --border-color: #ccc;
            --accent-color: #00b1de;
            --btn-bg: #e0e0e0;
            --btn-hover: #d0d0d0;
            --modal-bg: #fff;
            --range-track: #ccc;
        }
        #styleSetting.dark-mode {
            --bg-color: rgba(30, 30, 35, 0.98);
            --text-color: #eee;
            --border-color: #555;
            --accent-color: #f14e54;
            --btn-bg: #444;
            --btn-hover: #555;
            --modal-bg: #2a2a2a;
            --range-track: #555;
        }
        #styleSetting {
            padding: 15px; position: absolute; top: 10px; right: 10px; width: 340px;
            background-color: var(--bg-color); color: var(--text-color); display: none;
            border: 1px solid var(--border-color); border-radius: 8px;
            box-shadow: 0 4px 15px rgba(0,0,0,0.2);
            height: 85vh;
            display: flex; flex-direction: column; font-family: 'Ubuntu', sans-serif; font-size: 12px; z-index: 9999;
        }
        .header-container { display: flex; justify-content: space-between; align-items: center;
        }
        .title { font-weight: bold; font-size: 1.3rem; display: flex;
        align-items: center;}
        .subtitle { font-size: 0.8rem; color: #777; margin-bottom: 10px;
        }
        .pluginBody { flex-grow: 1; overflow-y: auto; padding-right: 5px;
        }
        .pluginBody.locked-ui { opacity: 0.6; pointer-events: none;
        }
        .row { display: flex; align-items: center; margin-bottom: 5px; padding: 2px 0;
        }
        .cell { flex: 1; } .label { flex: 2; font-weight: 500;
        }
        .value-display { text-align: right; padding-right: 10px; font-family: monospace;
        }
        .input-area { flex: 2; display: flex; align-items: center;
        }
        .check-area { justify-content: flex-end;
        }
        .font-select { width: 100%; padding: 4px; border-radius: 4px; border: 1px solid var(--border-color);
        background: var(--btn-bg); color: var(--text-color); }
        input[type=range] { -webkit-appearance: none; width: 100%; height: 12px;
        border-radius: 6px; background: var(--range-track); outline: none; background-image: linear-gradient(var(--accent-color), var(--accent-color)); background-repeat: no-repeat; background-size: 0% 100%; cursor: pointer;
        }
        input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; height: 18px; width: 18px; border-radius: 50%; background: #ffffff;
        box-shadow: 0 1px 3px rgba(0,0,0,0.4); cursor: pointer; margin-top: -3px; transition: transform 0.1s;
        }
        input[type=range]::-webkit-slider-thumb:hover { transform: scale(1.1);
        }
        hr { border: 0; border-top: 1px solid var(--border-color); margin: 10px 0;
        }
        .section-title { font-weight: bold; margin-bottom: 8px; text-transform: uppercase; font-size: 0.85rem; color: var(--accent-color);
        }
        .color-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
        }
        .color-item { display: flex; align-items: center; justify-content: space-between; border: 1px solid var(--border-color);
        padding: 4px; border-radius: 4px; background: rgba(0,0,0,0.05); }
        input.jscolor { width: 60px; border: none;
        text-align: center; border-radius: 3px; cursor: pointer; }
        .theme-controls { display: flex; gap: 5px;
        margin-bottom: 10px; flex-wrap: wrap; }
        #newThemeName { flex: 2; padding: 5px;
        border: 1px solid var(--border-color); border-radius: 4px; background: var(--modal-bg); color: var(--text-color); min-width: 120px;
        }
        #saveThemeBtn, #overwriteThemeBtn { flex: 1; padding: 5px; cursor: pointer; background: var(--accent-color);
        color: white; border: none; border-radius: 4px; white-space: nowrap; display: flex; align-items: center; justify-content: center; gap: 4px;
        }
        #overwriteThemeBtn { background: #f39c12;
        }
        #overwriteThemeBtn:disabled { background: var(--btn-bg); color: #999; cursor: not-allowed;
        }
        .theme-list { max-height: 200px; overflow-y: auto; border: 1px solid var(--border-color); border-radius: 4px;
        padding: 5px; }
        .theme-card { display: flex; justify-content: space-between; align-items: center; padding: 6px;
        border-bottom: 1px solid var(--border-color); transition: all 0.2s; }
        .theme-card.selected-theme { border-left: 4px solid #00e16e;
        background: rgba(0, 225, 110, 0.1); }
        .theme-card.market-card:hover { background: rgba(0,0,0,0.05) !important;
        }
        .theme-name { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 10px;
        }
        .theme-actions button { font-size: 0.75rem; margin-left: 4px; cursor: pointer; border-radius: 3px;
        border: 1px solid var(--border-color); padding: 2px 6px; background: var(--btn-bg); color: var(--text-color);}
        .load-theme-btn:disabled { opacity: 0.6;
        cursor: default; }
        .delete-theme-btn { color: white !important; background: #ff4757 !important;
        border: none !important; }
        .delete-theme-btn:hover { background: #e84118 !important;
        }
        .delete-theme-btn:disabled { background: #ccc !important;
        }
        .footer { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border-color);
        }
        .action-btns { display: flex; justify-content: space-between; gap: 5px; flex-wrap: wrap;
        }
        .action-btn { flex: 1 1 30%; padding: 8px; cursor: pointer; background: var(--btn-bg);
        color: var(--text-color); border: 1px solid var(--border-color); border-radius: 4px; font-size: 0.9rem; white-space: nowrap; display: flex; align-items: center; justify-content: center; gap: 4px;
        }
        .reset-btn:hover { background: #ff7979 !important;
        }
        .btn-icon { width: 14px; height: 14px; object-fit: contain;
        }
        .switch { position: relative; display: inline-block; width: 34px; height: 18px; margin-right: 5px;
        }
        .switch input { opacity: 0; width: 0; height: 0;
        }
        .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0;
        bottom: 0; background-color: #ccc; transition: .4s; border-radius: 34px; }
        .slider:before { position: absolute;
        content: ""; height: 14px; width: 14px; left: 2px; bottom: 2px; background-color: white; transition: .4s; border-radius: 50%;
        }
        input:checked + .slider { background-color: var(--accent-color);
        }
        input:checked + .slider:before { transform: translateX(16px);
        }
        .toggle-container { display: flex; align-items: center; font-size: 0.8rem;
        }
        .hide { display: none !important;
        }
        .ds-modal { position: fixed; z-index: 10000; left: 0; top: 0; width: 100%;
        height: 100%; overflow: auto; background-color: rgba(0,0,0,0.5); backdrop-filter: blur(2px); display: flex; align-items: center; justify-content: center;
        }
        .ds-modal-content { background-color: var(--modal-bg); color: var(--text-color); margin: auto; padding: 20px;
        border: 1px solid var(--border-color); width: 90%; max-width: 500px; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.3);
        }
        .ds-close, .ds-close-market, .ds-close-publish { color: #aaa; float: right; font-size: 28px; font-weight: bold;
        cursor: pointer; line-height: 20px;}
        .ds-close:hover, .ds-close-publish:hover { color: var(--text-color);
        }
        #ds-modal-text { width: 100%; height: 150px; margin: 10px 0; padding: 5px;
        background: var(--btn-bg); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 4px; resize: vertical; font-family: monospace; font-size: 0.8rem;
        }
        .ds-modal-footer { text-align: right;
        margin-top: 15px;}
        .ds-modal-footer button { padding: 8px 16px; background: var(--accent-color); color: white;
        border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; margin-left: 5px;}
        .form-group { margin-bottom: 10px;
        } .form-group label { display: block; margin-bottom: 5px; font-weight: bold; } .form-group input[type=text], .form-group input[type=file] { width: 100%; padding: 8px;
        border: 1px solid var(--border-color); border-radius: 4px; background: var(--btn-bg); color: var(--text-color); }
      `);
      function addGlobalStyle(css) {
        const head = document.getElementsByTagName("head")[0];
        if (!head) return;
        const style = document.createElement("style");
        style.type = "text/css";
        style.innerHTML = css;
        head.appendChild(style);
      }
    }
  }

  function togglePanel(tf) {
    const el = document.querySelector("#styleSetting");
    if (el) el.style.display = tf ? "flex" : "none";
  }

  function addScript(src) {
    const s = document.createElement("script");
    s.setAttribute("src", src);
    document.body.appendChild(s);
  }
})();