Wplace Map Themes

Switch Wplace map between 4 themes

// ==UserScript==
// @name           Wplace Map Themes
// @name:fr        Wplace Map Themes
// @namespace      https://github.com/CreepsoOff/wplace-dark-themes
// @version        0.0.1
// @description    Switch Wplace map between 4 themes
// @description:fr Basculer entre les 4 thèmes de la map wplace
// @author         Creepso
// @match          *://*.wplace.live/*
// @icon           https://www.google.com/s2/favicons?sz=64&domain=wplace.live
// @run-at         document-start
// @license        GPL-3.0 license
// ==/UserScript==

(() => {
  "use strict";

  // Available themes
  const themesMap = {
    liberty_wplace: {
      url: "https://maps.wplace.live/styles/liberty",
      iconColor: "#000000",
      buttonBackground: "#ffffff",
      name: "Liberty Wplace",
    },
    bright_wplace: {
      url: "https://maps.wplace.live/styles/bright",
      iconColor: "#000000",
      buttonBackground: "#ffffff",
      name: "Bright Wplace",
    },
    dark_black_wplace: {
      url: "https://maps.wplace.live/styles/dark",
      iconColor: "#ffffff",
      buttonBackground: "#000000",
      name: "Dark Black Wplace",
    },
    dark_blue_wplace: {
      url: "https://maps.wplace.live/styles/fiord",
      iconColor: "#ffffff",
      buttonBackground: "#000055",
      name: "Dark Blue Wplace",
    },
  };

  const defaultTheme = "liberty_wplace";
  const originalThemeUrl = "https://maps.wplace.live/styles/liberty";

  // Load stored theme or fallback to default
  const stored = localStorage.getItem("MapTheme");
  const mapTheme = stored && themesMap[stored] ? stored : defaultTheme;
  const selectedTheme = themesMap[mapTheme];

  // Override fetch to replace style URL
  const __ufetch = unsafeWindow.fetch;
  unsafeWindow.fetch = function (configArg, ...restArg) {
    const url =
      (typeof configArg === "string" && configArg) ||
      (configArg && configArg.url) ||
      "";
    if (url === originalThemeUrl) {
      return __ufetch(selectedTheme.url);
    }
    return __ufetch(configArg, ...restArg);
  };

  // Handle theme change
  unsafeWindow.changeMapTheme = function (event) {
    const theme = event.target.getAttribute("data-theme");
    if (!themesMap[theme]) return;
    localStorage.setItem("MapTheme", theme);
    location.reload();
  };

  // Add theme button and dropdown
  const observer = new MutationObserver((_, obs) => {
    const host = document.querySelector("div.flex.flex-col.items-center.gap-3");
    if (!host) return;

    obs.disconnect();

    const items = Object.entries(themesMap)
      .map(([id, t]) => {
        const active = mapTheme === id ? ' style="font-weight:600;"' : "";
        return `<li><a${active} data-theme="${id}" onclick="window.changeMapTheme(event);">${t.name}</a></li>`;
      })
      .join("");

    const mount = document.createElement("div");
    host.appendChild(mount);
    mount.outerHTML = `
      <div class="dropdown dropdown-end">
        <button class="btn btn-square relative shadow-md" tabindex="0" title="Map Theme"
                style="background-color:${selectedTheme.buttonBackground}">
          <svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"
               fill="${selectedTheme.iconColor}">
            <path d="M12 11.807A9.002 9.002 0 0 1 10.049 2a9.942 9.942 0 0 0-5.12 2.735c-3.905 3.905-3.905 10.237 0 14.142 3.906 3.906 10.237 3.905 14.143 0a9.946 9.946 0 0 0 2.735-5.119A9.003 9.003 0 0 1 12 11.807z"/>
          </svg>
        </button>
        <ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52">
          ${items}
        </ul>
      </div>
    `;
  });

  observer.observe(document, { childList: true, subtree: true });
})();