Drawaria Minecraft Drawer

A module to draw Minecraft-themed pixel art on the canvas using a pixel-to-line algorithm.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Drawaria Minecraft Drawer
// @namespace    drawaria.modded.minecraftdrawer
// @version      1.0.0
// @description  A module to draw Minecraft-themed pixel art on the canvas using a pixel-to-line algorithm.
// @author       YouTubeDrawaria
// @match        https://drawaria.online/*
// @grant        GM_addStyle
// @run-at       document-end
// @license      MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=drawaria.online
// @require      https://kit.fontawesome.com/a7a0b82b9e.js
// ==/UserScript==

(function() {
    'use strict';

    // --- Standalone Utilities and QBit Replacements ---

    // === 1. Notification System (replaces ModBase.notify) ===
    function notify(level, message, moduleName = "Minecraft Drawer") {
        let color = "#6c757d"; // log
        if (level === "error") color = "#dc3545";
        else if (level === "warning") color = "#ffc107";
        else if (level === "info") color = "#17a2b8";
        else if (level === "success") color = "#28a745";

        console.log(`%c${moduleName}: ${message}`, `color: ${color}`);

        const loggingContainer = document.getElementById("chatbox_messages");
        if (loggingContainer) {
            const chatmessage = document.createElement("div");
            chatmessage.className = `chatmessage systemchatmessage5`;
            chatmessage.dataset.ts = Date.now().toString();
            chatmessage.style.color = color;
            chatmessage.textContent = `${moduleName}: ${message}`;
            loggingContainer.appendChild(chatmessage);
            loggingContainer.scrollTop = loggingContainer.scrollHeight;
        }
    }

    // === 2. domMake Replacements (for building UI) ===
    function createElement(tag, attrs = {}, children = []) {
        const el = document.createElement(tag);
        for (const attr in attrs) {
            if (attr === "className") {
                el.className = attrs[attr];
            } else if (attr === "style") {
                 el.style.cssText = attrs[attr];
            } else if (attr === "title") {
                el.setAttribute("title", attrs[attr]);
            } else if (attr === "checked") {
                el.checked = attrs[attr];
            } else {
                el.setAttribute(attr, attrs[attr]);
            }
        }
        children.forEach(child => {
            if (typeof child === "string") {
                el.appendChild(document.createTextNode(child));
            } else if (child) {
                el.appendChild(child);
            }
        });
        return el;
    }

    function createButton(content, className = "artfx-button", attrs = {}) {
        const btn = createElement("button", { className: className, ...attrs });
        if (typeof content === "string") {
            btn.innerHTML = content;
        } else {
            btn.appendChild(content);
        }
        return btn;
    }

    // Simple div container creator (replaces domMake.Tree)
    function createTree(tag, attrs, children) {
        return createElement(tag, attrs, children);
    }

    // Simple row container creator (replaces domMake.Row)
    function createRow(attrs = {}, children = []) {
        const row = createElement("div", { className: "module-btn-group", ...attrs });
        children.forEach(child => {
            if (child) row.appendChild(child);
        });
        // Add a helper for appendAll if needed for complex structures, otherwise use standard appendChild
        row.appendAll = (...elements) => elements.forEach(el => row.appendChild(el));
        return row;
    }


    // === 3. Socket Management (replaces globalThis.sockets and _getGameSocket) ===
    const activeSockets = [];
    if (window.WebSocket) {
        const originalWebSocketSend = WebSocket.prototype.send;
        WebSocket.prototype.send = function(...args) {
            if (this.url.includes("drawaria.online/socket.io") && activeSockets.indexOf(this) === -1) {
                activeSockets.push(this);
                this.addEventListener('close', () => {
                    const pos = activeSockets.indexOf(this);
                    if (~pos) activeSockets.splice(pos, 1);
                });
            }
            return originalWebSocketSend.apply(this, args);
        };
    }

    function getPrimarySocket(skipNotification = false) {
        const primarySocket = activeSockets.find(s => s.readyState === WebSocket.OPEN);
        if (!primarySocket) {
            if (!skipNotification) notify("warning", "No active WebSocket connections found.", "Minecraft Drawer");
            return null;
        }
        return primarySocket;
    }

    // === 4. General Utilities ===
    const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
    const clamp = (value, min, max) => Math.max(min, Math.min(max, value));

    // --- Adapted MinecraftDrawer Class ---
    class MinecraftDrawerStandalone {

      _identifier = 'minecraftdrawer';
      _containerId = `${this._identifier}-menu-container`;
      _contentId = `${this._identifier}-content`;

      _minecraftImages = {
        mobs: [
          "https://minecraft.wiki/images/Allay_JE1_BE1.png",
          "https://minecraft.wiki/images/Cod.png",
          "https://minecraft.wiki/images/Salmon.png",
          "https://minecraft.wiki/images/Skeleton_Horse.png",
          "https://minecraft.wiki/images/Strider.png",
          "https://minecraft.wiki/images/Wandering_Trader.png",
          "https://minecraft.wiki/images/Bee.png",
          "https://minecraft.wiki/images/Dolphin.png",
          "https://minecraft.wiki/images/Drowned.png",
          "https://minecraft.wiki/images/Iron_Golem_JE2_BE2.png",
          "https://minecraft.wiki/images/Llama.png",
          "https://minecraft.wiki/images/Piglin.png",
          "https://minecraft.wiki/images/Endermite.png",
          "https://minecraft.wiki/images/Ender_Dragon.png",
          "https://minecraft.wiki/images/Zombie_Villager.png",
          "https://minecraft.wiki/images/Skeleton_Horseman.png",
          "https://minecraft.wiki/images/Agent.png",
          "https://minecraft.wiki/images/Beast_Boy.png",
          "https://minecraft.wiki/images/Rana.png",
          "https://minecraft.wiki/images/Diamond_Chicken.png",
          "https://minecraft.wiki/images/Mars.png",
          "https://minecraft.wiki/images/Moobloom.png",
          "https://minecraft.wiki/images/Moon_Cow.png",
          "https://minecraft.wiki/images/Friendly_Wither.png",
          "https://minecraft.wiki/images/Redstone_Bug.png",
          "https://minecraft.wiki/images/Smiling_Creeper.png",
          "https://minecraft.wiki/images/Pigman.png",
          "https://minecraft.wiki/images/Chinese_Alligator.png",
          "https://minecraft.wiki/images/Golden_Monkey.png",
          "https://minecraft.wiki/images/White-Lipped_Deer.png",
          "https://minecraft.wiki/images/Iceologer.png",
          "https://minecraft.wiki/images/Glare.png"
        ],
        items: [
          "https://minecraft.wiki/images/Wheat.png",
          "https://minecraft.wiki/images/Map.png",
          "https://minecraft.wiki/images/Spawn_Egg.png"
        ],
        blocks: [
          "https://minecraft.wiki/images/Dirt.png"
        ]
      };

      _currentCategory = 'mobs';
      _currentIndex = 0;
      _canvasBrushImg = null;
      _ui = {};
      _mainCanvas = null;
      _canvasClickHandler = null;
      _canvasClickHandlerAttached = false;
      _isActive = false;
      _isDrawingCube = false;


      constructor() {
        this._canvasClickHandler = this._handleCanvasClick.bind(this);
        this._createMenuStructure();
        this._onStartup();
      }

      _onStartup() {
        this._mainCanvas = document.getElementById("canvas");
        this._loadInterface();
        this._setupEventListeners();
        this._loadMinecraftImage(this._currentCategory, this._currentIndex);
        this._setModuleActive(false);
        notify("info", " 'Minecraft Drawer' Loaded. Click the gem icon to open.", "Minecraft Drawer");
      }

      _createMenuStructure() {
          const menuContainer = createTree("div", {
              id: this._containerId,
              className: "minecraft-drawer-menu",
              style: `
                  position: fixed;
                  top: 10%;
                  left: 50px;
                  width: 300px;
                  display: none; /* Starts hidden */
              `
          });

          const header = createTree("div", {
              className: "minecraft-drawer-menu-header",
              style: `
                  padding: 8px;
                  background-color: var(--primary, #007bff);
                  color: white;
                  cursor: grab;
                  border-top-left-radius: .25rem;
                  border-top-right-radius: .25rem;
                  display: flex;
                  justify-content: space-between;
                  align-items: center;
              `
          }, [createTree("span", {}, ["💎 Minecraft Toolkit"]), createButton("X", "btn-close-menu")]);

          header.querySelector(".btn-close-menu").addEventListener("click", () => {
              menuContainer.style.display = "none";
              this._setModuleActive(false); // Deactivate module when closing
          });

          const content = createTree("div", {
              id: this._contentId,
              className: "minecraft-drawer-menu-content",
              style: `padding: 10px;`
          });

          menuContainer.append(header, content);
          document.body.appendChild(menuContainer);
          this._makeDraggable(menuContainer, header);

          this._ui.menuContainer = menuContainer;
          this._ui.section = content;
          this._ui.header = header;
      }

      _makeDraggable(element, handle = element) {
          let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

          const dragMouseDown = (e) => {
              e = e || window.event;
              e.preventDefault();
              pos3 = e.clientX;
              pos4 = e.clientY;
              document.onmouseup = closeDragElement;
              document.onmousemove = elementDrag;
          };

          const elementDrag = (e) => {
              e = e || window.event;
              e.preventDefault();
              pos1 = pos3 - e.clientX;
              pos2 = pos4 - e.clientY;
              pos3 = e.clientX;
              pos4 = e.clientY;
              element.style.top = (element.offsetTop - pos2) + "px";
              element.style.left = (element.offsetLeft - pos1) + "px";
          };

          const closeDragElement = () => {
              document.onmouseup = null;
              document.onmousemove = null;
          };

          handle.onmousedown = dragMouseDown;
      }


      _loadInterface() {
        const container = createTree("div", {
          id: `${this._identifier}-container`,
          className: "module-section"
        });
        this._ui.section.appendChild(container);


        const moduleToggleGroup = createTree("div", {
          className: "module-btn-group",
          style: "margin-bottom:10px;"
        });
        this._ui.moduleToggleButton = createButton('<i class="fas fa-power-off"></i> Activar Módulo', 'module-toggle-button');
        this._ui.moduleToggleButton.addEventListener('click', () => this._toggleModuleActive());
        moduleToggleGroup.appendChild(this._ui.moduleToggleButton);
        container.appendChild(moduleToggleGroup);

        container.appendChild(createTree("div", {
          className: "module-section-title"
        }, ["Minecraft Online Drawer"]));


        container.appendChild(createTree("div", {
          className: "module-section-title",
          style: "margin-top:15px;"
        }, ["Seleccionar Categoría"]));
        const categoryButtonGroup = createTree("div", {
          className: "module-btn-group",
          style: "margin-bottom:10px;"
        });
        this._ui.mobsCategoryBtn = createButton('Mobs', {
          className: "artfx-button category-nav-button"
        });
        this._ui.itemsCategoryBtn = createButton('Items', {
          className: "artfx-button category-nav-button"
        });
        this._ui.blocksCategoryBtn = createButton('Bloques', {
          className: "artfx-button category-nav-button"
        });

        this._ui.mobsCategoryBtn.addEventListener('click', () => this._setCategory('mobs'));
        this._ui.itemsCategoryBtn.addEventListener('click', () => this._setCategory('items'));
        this._ui.blocksCategoryBtn.addEventListener('click', () => this._setCategory('blocks'));

        categoryButtonGroup.append(this._ui.mobsCategoryBtn, this._ui.itemsCategoryBtn, this._ui.blocksCategoryBtn);
        container.appendChild(categoryButtonGroup);


        const navRow = createTree("div", {
          style: "display:flex;align-items:center;justify-content:center;gap:10px;margin-bottom:10px;"
        });
        this._ui.prevBtn = createButton('←', {
          className: "artfx-button special"
        });
        this._ui.canvas = createTree("canvas", {
          width: 96,
          height: 96,
          style: "border:1px solid var(--CE-color);background:#222;"
        });
        this._ui.nextBtn = createButton('→', {
          className: "artfx-button special"
        });
        navRow.append(this._ui.prevBtn, this._ui.canvas, this._ui.nextBtn);
        container.appendChild(navRow);


        this._ui.info = createTree("div", {
          style: "text-align:center;margin-top:5px;font-size:0.9em;color:var(--info);"
        });
        container.appendChild(this._ui.info);


        container.appendChild(createTree("div", {
          className: "module-section-title",
          style: "margin-top:15px;"
        }, ["Buscar por ID"]));
        const searchRow = createRow({
          style: "display:flex;align-items:center;gap:5px;"
        });
        this._ui.iconIdInput = createTree("input", {
          type: "number",
          id: `${this._identifier}-iconIdInput`,
          min: "1",
          value: "1",
          placeholder: "ID del Objeto",
          className: "module-form-control",
          style: "flex:1 1 60%;"
        });
        this._ui.acceptIconBtn = createButton('Aceptar ID', {
          className: "artfx-button",
          style: "flex:1 1 35%;"
        });
        searchRow.appendAll(this._ui.iconIdInput, this._ui.acceptIconBtn);
        container.appendChild(searchRow);


        const randomRow = createRow({
          style: "margin-top:10px;"
        });
        this._ui.randomIconBtn = createButton('Objeto Random', {
          className: "artfx-button special",
          style: "width:100%;"
        });
        randomRow.appendChild(this._ui.randomIconBtn);
        container.appendChild(randomRow);


        container.appendChild(createTree("div", {
          className: "module-section-title",
          style: "margin-top:15px;"
        }, ["Optimización de Dibujo"]));
        const optSettingsGrid = createTree("div", {
          className: "module-btn-group",
          style: "flex-wrap:wrap;"
        });


        // Delay Segment
        this._ui.delayPerSegmentInput = createTree("input", {
          type: "number",
          id: `${this._identifier}-delayPerSegmentInput`,
          min: "0",
          max: "50",
          value: "120",
          title: "Retraso por segmento de línea (ms)",
          className: "module-form-control"
        });
        this._ui.delayPerSegmentLabel = createTree("label", {
          for: `${this._identifier}-delayPerSegmentInput`
        }, ["Delay Segmento (ms):"]);
        optSettingsGrid.append(createTree("div", {
          style: "flex:1 1 48%;"
        }, [this._ui.delayPerSegmentLabel, this._ui.delayPerSegmentInput]));

        // Delay Row
        this._ui.delayPerRowInput = createTree("input", {
          type: "number",
          id: `${this._identifier}-delayPerRowInput`,
          min: "0",
          max: "200",
          value: "130",
          title: "Retraso por fila de píxeles (ms)",
          className: "module-form-control"
        });
        this._ui.delayPerRowLabel = createTree("label", {
          for: `${this._identifier}-delayPerRowInput`
        }, ["Delay Fila (ms):"]);
        optSettingsGrid.append(createTree("div", {
          style: "flex:1 1 48%;"
        }, [this._ui.delayPerRowLabel, this._ui.delayPerRowInput]));

        // Quality Factor
        this._ui.qualityFactorInput = createTree("input", {
          type: "number",
          id: `${this._identifier}-qualityFactorInput`,
          min: "1",
          max: "5",
          value: "6",
          title: "Factor de calidad (1=mejor, 5=peor, más rápido)",
          className: "module-form-control"
        });
        this._ui.qualityFactorLabel = createTree("label", {
          for: `${this._identifier}-qualityFactorInput`
        }, ["Calidad (1-5):"]);
        optSettingsGrid.append(createTree("div", {
          style: "flex:1 1 48%;"
        }, [this._ui.qualityFactorLabel, this._ui.qualityFactorInput]));

        // Auto Clear Toggle
        this._ui.autoClearBeforeDrawToggle = createTree("input", {
          type: "checkbox",
          id: `${this._identifier}-autoClearToggle`,
          checked: false,
          title: "Limpiar el canvas antes de dibujar cada objeto",
          style: "margin-right: 5px; transform: scale(1.5);"
        });

        this._ui.autoClearBeforeDrawLabel = createTree("label", {
          for: `${this._identifier}-autoClearToggle`,
          style: "flex-grow: 1; text-align: left;"
        }, ["Auto-Limpiar antes de dibujar"]);
        optSettingsGrid.append(createTree("div", {
          style: "flex:1 1 48%; display:flex; align-items:center; flex-direction:row;"
        }, [this._ui.autoClearBeforeDrawToggle, this._ui.autoClearBeforeDrawLabel]));

        container.appendChild(optSettingsGrid);


        this._ui.status = createTree("div", {
          style: "text-align:center;margin-top:10px;font-size:0.85em;color:var(--info);"
        }, ["Haz clic en el canvas principal para dibujar el objeto donde hiciste clic (será visible para todos)."]);
        container.appendChild(this._ui.status);


        this._updateCategoryButtons();
      }

      _setupEventListeners() {
        this._ui.prevBtn.addEventListener('click', () => this._changeImage(-1));
        this._ui.nextBtn.addEventListener('click', () => this._changeImage(1));


        this._ui.acceptIconBtn.addEventListener('click', () => this._loadMinecraftImage(this._currentCategory, parseInt(this._ui.iconIdInput.value) - 1 || 0));
        this._ui.randomIconBtn.addEventListener('click', () => {
          const imagesInCurrentCategory = this._minecraftImages[this._currentCategory];
          if (imagesInCurrentCategory.length > 0) {
            const randomIndex = Math.floor(Math.random() * imagesInCurrentCategory.length);
            this._loadMinecraftImage(this._currentCategory, randomIndex);
          } else {
            notify("warning", "No hay imágenes en la categoría actual para seleccionar al azar.");
          }
        });
      }


      _toggleModuleActive() {
        this._setModuleActive(!this._isActive);
      }


      _setModuleActive(active) {
        this._isActive = active;
        if (active) {
          this._hookCanvasClick();
          this._ui.moduleToggleButton.innerHTML = '<i class="fas fa-power-off"></i> Desactivar Módulo';
          this._ui.moduleToggleButton.classList.add('active');
          this._ui.status.textContent = "Listo. Haz click en el canvas principal para dibujar.";

        } else {
          this._unhookCanvasClick();
          this._ui.moduleToggleButton.innerHTML = '<i class="fas fa-power-off"></i> Activar Módulo';
          this._ui.moduleToggleButton.classList.remove('active');
          this._isDrawingCube = false;
          this._ui.status.textContent = "Módulo inactivo. Actívalo para usarlo.";
        }

        const controlsToToggle = [
            this._ui.prevBtn, this._ui.nextBtn, this._ui.iconIdInput, this._ui.acceptIconBtn,
            this._ui.randomIconBtn, this._ui.mobsCategoryBtn, this._ui.itemsCategoryBtn,
            this._ui.blocksCategoryBtn, this._ui.delayPerSegmentInput, this._ui.delayPerRowInput,
            this._ui.qualityFactorInput, this._ui.autoClearBeforeDrawToggle
        ];

        controlsToToggle.forEach(el => {
          if (el) el.disabled = !active;
        });

        this._ui.canvas.style.opacity = active ? '1' : '0.5';
      }


      _setCategory(category) {
        if (this._currentCategory === category) return;

        this._currentCategory = category;
        this._currentIndex = 0;
        this._updateCategoryButtons();
        this._loadMinecraftImage(this._currentCategory, this._currentIndex);
      }


      _updateCategoryButtons() {
        this._ui.mobsCategoryBtn.classList.toggle('active', this._currentCategory === 'mobs');
        this._ui.itemsCategoryBtn.classList.toggle('active', this._currentCategory === 'items');
        this._ui.blocksCategoryBtn.classList.toggle('active', this._currentCategory === 'blocks');
      }

      _changeImage(delta) {
        const imagesInCurrentCategory = this._minecraftImages[this._currentCategory];
        if (imagesInCurrentCategory.length === 0) {
          notify("warning", "La categoría actual no tiene imágenes.");
          return;
        }

        let newIndex = this._currentIndex + delta;

        if (newIndex < 0) newIndex = imagesInCurrentCategory.length - 1;
        if (newIndex >= imagesInCurrentCategory.length) newIndex = 0;

        this._loadMinecraftImage(this._currentCategory, newIndex);
      }

      _updateInfo() {
        const imagesInCurrentCategory = this._minecraftImages[this._currentCategory];
        const categoryNameMap = {
          mobs: "Mob",
          items: "Item",
          blocks: "Bloque"
        };
        const categoryDisplayName = categoryNameMap[this._currentCategory] || "Objeto";
        const maxIndex = imagesInCurrentCategory.length - 1;

        this._ui.info.textContent = `${categoryDisplayName} #${this._currentIndex + 1} / ${maxIndex + 1}`;
        this._ui.iconIdInput.max = maxIndex + 1;
      }

      _loadMinecraftImage(category, index) {
        const imagesInCurrentCategory = this._minecraftImages[category];
        if (!imagesInCurrentCategory || imagesInCurrentCategory.length === 0) {
          const ctx = this._ui.canvas.getContext("2d");
          ctx.clearRect(0, 0, 96, 96);
          ctx.fillStyle = "var(--danger)";
          ctx.font = "14px Arial";
          ctx.fillText("NO IMG", 18, 55);
          this._canvasBrushImg = null;
          this._ui.status.textContent = `Error: No hay imágenes para ${category}.`;
          this._updateInfo();
          return;
        }

        index = clamp(index, 0, imagesInCurrentCategory.length - 1);
        this._currentCategory = category;
        this._currentIndex = index;
        this._updateInfo();
        this._ui.iconIdInput.value = index + 1;

        const ctx = this._ui.canvas.getContext("2d");
        ctx.clearRect(0, 0, 96, 96);
        const img = new window.Image();
        img.crossOrigin = "anonymous";
        img.onload = () => {
          ctx.clearRect(0, 0, 96, 96);
          ctx.drawImage(img, 0, 0, 96, 96); // Draw to 96x96 preview canvas
          this._canvasBrushImg = img;
          if(this._isActive) this._ui.status.textContent = "Listo. Haz click en el canvas principal para dibujar.";
        };
        img.onerror = (e) => {
          ctx.clearRect(0, 0, 96, 96);
          ctx.fillStyle = "var(--danger)";
          ctx.font = "14px Arial";
          ctx.fillText("NO IMG", 18, 55);
          this._canvasBrushImg = null;
          const failedUrl = imagesInCurrentCategory[index];
          console.error(`Error al cargar imagen de Minecraft (URL: ${failedUrl}):`, e);
          notify("error", "Error al cargar imagen del objeto.");
          this._ui.status.textContent = "Error al cargar imagen del objeto.";
        };
        img.src = imagesInCurrentCategory[index];
      }


      async _handleCanvasClick(ev) {
        if (this._isDrawingCube || !this._isActive || !this._canvasBrushImg || !this._mainCanvas) {
            return;
        }

        const socket = getPrimarySocket();
        if (!socket) {
            this._ui.status.textContent = "¡Sin conexión! (Revisa la consola)";
            return;
        }

        this._isDrawingCube = true;

        const rect = this._mainCanvas.getBoundingClientRect();
        const clickX_display_px = ev.clientX - rect.left;
        const clickY_display_px = ev.clientY - rect.top;


        const scaleToInternalCanvas = this._mainCanvas.width / rect.width;
        const clickX_internal_px = clickX_display_px * scaleToInternalCanvas;
        const clickY_internal_px = clickY_display_px * scaleToInternalCanvas;


        const delayPerSegment = parseInt(this._ui.delayPerSegmentInput.value) || 80;
        const delayPerRow = parseInt(this._ui.delayPerRowInput.value) || 100;
        const qualityFactor = parseInt(this._ui.qualityFactorInput.value) || 6;
        const autoClear = this._ui.autoClearBeforeDrawToggle.checked;

        this._ui.status.textContent = "Pintando objeto Minecraft...";


        if (autoClear) {
          await this._clearCanvas(socket);
          await delay(100);
        }


        const tempCanvas = document.createElement("canvas");
        tempCanvas.width = this._canvasBrushImg.width;
        tempCanvas.height = this._canvasBrushImg.height;
        const tempCtx = tempCanvas.getContext("2d");
        tempCtx.drawImage(this._canvasBrushImg, 0, 0);

        const imgData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height).data;


        const targetImageWidth_px = 64;
        const scaleFactor_drawing = targetImageWidth_px / tempCanvas.width;

        let currentLineStart_asset_px = null;
        let currentLineColor = null;
        let totalLinesDrawn = 0;

        for (let py = 0; py < tempCanvas.height; py += qualityFactor) {
          if (!this._isActive || !this._isDrawingCube) {
            break;
          }

          currentLineStart_asset_px = null;
          currentLineColor = null;

          for (let px = 0; px < tempCanvas.width; px += qualityFactor) {
            const idx = (py * tempCanvas.width + px) * 4;
            const r = imgData[idx],
              g = imgData[idx + 1],
              b = imgData[idx + 2],
              a = imgData[idx + 3];
            const hexColor = "#" + [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('');

            if (a > 10) {
              if (currentLineStart_asset_px === null) {
                currentLineStart_asset_px = px;
                currentLineColor = hexColor;
              } else if (hexColor !== currentLineColor) {

                const startX_draw = clickX_internal_px + (currentLineStart_asset_px - tempCanvas.width / 2) * scaleFactor_drawing;
                const startY_draw = clickY_internal_px + (py - tempCanvas.height / 2) * scaleFactor_drawing;
                const endX_draw = clickX_internal_px + (px - qualityFactor - tempCanvas.width / 2) * scaleFactor_drawing;
                const endY_draw = startY_draw;

                const thickness = Math.max(1, Math.round(scaleFactor_drawing * qualityFactor));
                this._sendDrawCommand(socket, [startX_draw, startY_draw], [endX_draw, endY_draw], currentLineColor, thickness);
                totalLinesDrawn++;

                currentLineStart_asset_px = px;
                currentLineColor = hexColor;
              }
            } else {
              if (currentLineStart_asset_px !== null) {

                const startX_draw = clickX_internal_px + (currentLineStart_asset_px - tempCanvas.width / 2) * scaleFactor_drawing;
                const startY_draw = clickY_internal_px + (py - tempCanvas.height / 2) * scaleFactor_drawing;
                const endX_draw = clickX_internal_px + (px - qualityFactor - tempCanvas.width / 2) * scaleFactor_drawing;
                const endY_draw = startY_draw;

                const thickness = Math.max(1, Math.round(scaleFactor_drawing * qualityFactor));
                this._sendDrawCommand(socket, [startX_draw, startY_draw], [endX_draw, endY_draw], currentLineColor, thickness);
                totalLinesDrawn++;

                currentLineStart_asset_px = null;
                currentLineColor = null;
              }
            }
          }

          // Draw the remaining segment at the end of the row
          if (currentLineStart_asset_px !== null) {
            const startX_draw = clickX_internal_px + (currentLineStart_asset_px - tempCanvas.width / 2) * scaleFactor_drawing;
            const startY_draw = clickY_internal_px + (py - tempCanvas.height / 2) * scaleFactor_drawing;
            const endX_draw = clickX_internal_px + (tempCanvas.width - 1 - tempCanvas.width / 2) * scaleFactor_drawing;
            const endY_draw = startY_draw;

            const thickness = Math.max(1, Math.round(scaleFactor_drawing * qualityFactor));
            this._sendDrawCommand(socket, [startX_draw, startY_draw], [endX_draw, endY_draw], currentLineColor, thickness);
            totalLinesDrawn++;

            currentLineStart_asset_px = null;
            currentLineColor = null;
          }

          await delay(delayPerRow);
        }

        this._isDrawingCube = false;
        notify("success", `Objeto Minecraft dibujado en el canvas (${totalLinesDrawn} líneas). Visible para todos.`);
        this._ui.status.textContent = "Listo. Haz click de nuevo para otro objeto.";
      }


      _hookCanvasClick() {

        if (!this._mainCanvas) {
          this._mainCanvas = document.getElementById("canvas");
          if (!this._mainCanvas) {
            return;
          }
        }

        if (!this._canvasClickHandlerAttached) {
          this._mainCanvas.addEventListener("click", this._canvasClickHandler);
          this._canvasClickHandlerAttached = true;
          notify("info", "Canvas click handler attached.", "Minecraft Drawer");
        }
      }


      _unhookCanvasClick() {
        if (this._mainCanvas && this._canvasClickHandlerAttached) {
          this._mainCanvas.removeEventListener("click", this._canvasClickHandler);
          this._canvasClickHandlerAttached = false;
          notify("info", "Canvas click handler detached.", "Minecraft Drawer");
        }
      }


      _sendDrawCommand(socket, start_px, end_px, color, thickness, isEraser = false) {

        if (!this._mainCanvas) {
          console.error("Error: _mainCanvas is null in _sendDrawCommand. Cannot send drawing command.");
          return;
        }


        const normX1 = (start_px[0] / this._mainCanvas.width);
        const normY1 = (start_px[1] / this._mainCanvas.height);
        const normX2 = (end_px[0] / this._mainCanvas.width);
        const normY2 = (end_px[1] / this._mainCanvas.height);


        const ctx = this._mainCanvas.getContext('2d');
        ctx.strokeStyle = color;
        ctx.lineWidth = thickness;
        ctx.lineCap = 'round';
        ctx.beginPath();
        ctx.moveTo(start_px[0], start_px[1]);
        ctx.lineTo(end_px[0], end_px[1]);
        ctx.stroke();



        socket.send(`42["drawcmd",0,[${normX1.toFixed(4)},${normY1.toFixed(4)},${normX2.toFixed(4)},${normY2.toFixed(4)},${isEraser},${0 - thickness},"${color}",0,0,{}]]`);
      }


      async _clearCanvas(socket) {
        if (!this._mainCanvas || !this._mainCanvas.getContext('2d')) {
          return;
        }
        if (!socket || socket.readyState !== WebSocket.OPEN) {
          return;
        }

        const ctx = this._mainCanvas.getContext('2d');
        ctx.clearRect(0, 0, this._mainCanvas.width, this._mainCanvas.height);


        const w = this._mainCanvas.width;
        const h = this._mainCanvas.height;
        const clearThickness = 2000;
        const clearColor = '#FFFFFF';
        const steps = 5;

        for (let i = 0; i <= steps; i++) {
          // Horizontal sweep (using pixel coordinates for the internal drawing command)
          this._sendDrawCommand(socket, [0, (i / steps) * h], [w, (i / steps) * h], clearColor, clearThickness, true);
          await delay(5);
          // Vertical sweep (using pixel coordinates for the internal drawing command)
          this._sendDrawCommand(socket, [(i / steps) * w, 0], [(i / steps) * w, h], clearColor, clearThickness, true);
          await delay(5);
        }
        notify("success", "Lienzo limpiado para todos.");
      }
    }

    // --- Style Injection ---
    GM_addStyle(`
        /* Custom Variables for standalone appearance, mimicking CE */
        :root {
            --light: #f8f9fa;
            --dark: #212529;
            --primary: #007bff;
            --secondary: #6c757d;
            --success: #28a745;
            --info: #17a2b8;
            --danger: #dc3545;
            --warning: #ffc107;

            /* Minecraft-Themed CE-like Variables */
            --CE-bg_color: #1c1c1c; /* Very dark background */
            --CE-color: #f8f9fa;   /* Light text color */
            --CE-input-bg: #444;   /* Dark input background */
            --CE-btn-special: #5cb85c; /* Green color (Minecraft theme) */
            --CE-btn-active: #4a8c4a;   /* Darker green active button */
        }

        .minecraft-drawer-menu {
            line-height: normal;
            font-size: 1rem;
            background-color: var(--CE-bg_color);
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            box-shadow: 0 4px 8px rgba(0,0,0,0.5);
            z-index: 9999;
            font-family: Arial, sans-serif;
            color: var(--CE-color);
            max-height: 80vh;
            overflow-y: auto;
        }

        .minecraft-drawer-menu-header {
            cursor: grab;
            background-color: #3f7f3f !important; /* A distinct Minecraft green */
        }

        .minecraft-drawer-menu .btn-close-menu {
            background-color: var(--danger);
            color: white;
            padding: 3px 8px;
            font-size: 0.8em;
            border: none;
            line-height: 1;
            border-radius: .25rem;
            cursor: pointer;
        }

        .module-section-title {
            text-align: center;
            font-weight: bold;
            color: var(--CE-btn-special);
            margin: 10px 0;
            font-size: 1.1em;
            border-bottom: 1px dashed var(--CE-color);
            padding-bottom: 5px;
        }

        .module-btn-group {
            display: flex;
            gap: 5px;
            margin-bottom: 5px;
        }

        /* General Button Style */
        .artfx-button {
            padding: 5px 10px;
            font-size: 0.9em;
            cursor: pointer;
            border: 1px solid var(--secondary);
            border-radius: .25rem;
            background-color: var(--secondary);
            color: var(--dark);
            transition: background-color 0.2s, color 0.2s, border-color 0.2s;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            flex: 1;
        }
        .artfx-button.special {
            background-color: var(--CE-btn-special);
            color: white;
            border-color: var(--CE-btn-special);
        }
        .artfx-button:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }
        .artfx-button i {
            margin-right: 5px;
        }

        /* Category Buttons */
        .category-nav-button {
            flex: 1 1 30%;
            font-size: 0.9em;
            padding: 5px;
            background-color: var(--CE-input-bg);
            color: var(--CE-color);
            border-color: var(--CE-input-bg);
        }
        .category-nav-button.active {
            background-color: var(--CE-btn-active);
            color: white;
            border-color: var(--CE-btn-active);
        }

        /* Toggle Button */
        .module-toggle-button {
            width: 100%;
            background-color: var(--danger);
            color: white;
            font-weight: bold;
        }
        .module-toggle-button.active {
            background-color: var(--success);
        }

        /* Form Controls */
        .module-form-control {
            padding: 5px;
            border: 1px solid var(--secondary);
            border-radius: .25rem;
            background-color: var(--CE-input-bg);
            color: var(--CE-color);
            text-align: center;
        }

        /* Input and Label Layout */
        .module-btn-group > div {
            display: flex;
            flex-direction: column;
            gap: 2px;
        }
        .module-btn-group > div label {
            font-size: 0.8em;
            color: var(--CE-color);
        }
        .module-btn-group > div input[type="number"] {
             width: 100%;
        }
    `);

    // --- Module Initialization ---
    function initializeMinecraftDrawer() {
        if (document.body && document.getElementById("canvas")) {
            const drawer = new MinecraftDrawerStandalone();

            // Floating toggle button
            const toggleButton = createElement("button", {
                id: "open-minecraft-drawer-menu",
                style: `
                    position: fixed;
                    bottom: 80px;
                    right: 20px;
                    width: 50px;
                    height: 50px;
                    border-radius: 50%;
                    background-color: #3f7f3f; /* Minecraft Green */
                    color: white;
                    border: none;
                    box-shadow: 0 2px 5px rgba(0,0,0,0.3);
                    font-size: 1.5em;
                    cursor: pointer;
                    z-index: 9998;
                `
            }, ['💎']);

            document.body.appendChild(toggleButton);

            toggleButton.addEventListener("click", () => {
                const menu = document.getElementById(drawer._containerId);
                if (menu) {
                    const isVisible = menu.style.display === "block";
                    menu.style.display = isVisible ? "none" : "block";
                    if (!isVisible) {
                        notify("info", "Minecraft Drawer Menu opened.", "Minecraft Drawer");
                    } else {
                        notify("info", "Minecraft Drawer Menu closed.", "Minecraft Drawer");
                        drawer._setModuleActive(false); // Deactivate module when closing
                    }
                }
            });

        } else {
            setTimeout(initializeMinecraftDrawer, 100); // Retry if body or canvas is not ready yet
        }
    }

    initializeMinecraftDrawer();

})();