Wipo

Pixelplace.io tuvalinde WebSocket üzerinden hızlı piksel yerleştirme ve görsel aktarımı sağlayan gelişmiş otomasyon botu.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Wipo
// @namespace    http://tampermonkey.net/
// @version      2.0.3
// @description  Pixelplace.io tuvalinde WebSocket üzerinden hızlı piksel yerleştirme ve görsel aktarımı sağlayan gelişmiş otomasyon botu.
// @author       Kiwe
// @match        https://pixelplace.io/*
// @icon         https://pixelplace.io/favicon.ico
// @require      https://pixelplace.io/js/jquery.min.js?v2=1
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      pixelplace.io
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    console.log('[Wipo] v2.0.3 by Kiwe — Pixelplace.io bot loaded.');

    // ----- 64 RENK PALET -----
    var PALETTE = [
        [0, 0, 0], [29, 43, 83], [126, 37, 83], [0, 135, 81],
        [171, 82, 54], [95, 87, 79], [194, 195, 199], [255, 241, 231],
        [255, 0, 77], [255, 163, 0], [255, 236, 39], [0, 228, 54],
        [41, 173, 255], [131, 118, 255], [255, 119, 168], [255, 204, 170],
        [34, 34, 34], [76, 82, 106], [216, 76, 120], [67, 179, 113],
        [208, 120, 86], [138, 131, 123], [223, 223, 223], [255, 248, 240],
        [255, 80, 120], [255, 190, 40], [255, 236, 39], [0, 228, 54],
        [80, 200, 255], [170, 160, 255], [255, 160, 190], [255, 220, 200],
        [50, 50, 50], [100, 100, 100], [150, 150, 150], [200, 200, 200],
        [255, 100, 100], [100, 255, 100], [100, 100, 255], [255, 255, 100],
        [255, 100, 255], [100, 255, 255], [200, 150, 100], [150, 200, 100],
        [100, 150, 200], [200, 100, 150], [150, 100, 200], [100, 200, 150],
        [200, 200, 150], [150, 200, 200], [200, 150, 200], [150, 150, 200],
        [255, 200, 150], [150, 255, 200], [200, 150, 255], [150, 200, 255],
        [255, 150, 200], [200, 255, 150], [150, 255, 200], [255, 200, 100],
        [100, 200, 255], [200, 100, 255], [255, 100, 200], [200, 255, 100]
    ];

    // ----- ORTAK PIXEL TYPE -----
    function determinePixelType(x, y, color, type) {
        type = type || 'default';
        var types = {
            default: '[' + x + ',' + y + ',' + color + ',1]',
            protect: '[' + x + ',' + y + ',' + color + ',1,1]',
            seaprotect: '[' + x + ',' + y + ',-100,1,1]',
            unprotect: '[' + x + ',' + y + ',' + color + ',1,2]',
            replace: '[' + x + ',' + y + ',' + color + ',1,3]'
        };
        if (!(type in types)) type = 'default';
        return types[type];
    }

    // ----- DITHERING (SINIR KONTROLLU) -----
    var DITHER = {
        none: function() {},
        floyd: function(errR, errG, errB, x, y, w, h, data) {
            var eR = errR / 16, eG = errG / 16, eB = errB / 16;
            var i = (y * w + x) * 4;
            if (x + 1 < w && i + 4 < data.length) {
                data[i+4] += eR * 7; data[i+5] += eG * 7; data[i+6] += eB * 7;
            }
            if (y + 1 < h) {
                var j = ((y + 1) * w + x) * 4;
                if (j < data.length) {
                    if (x > 0 && j - 4 >= 0) {
                        data[j-4] += eR * 3; data[j-3] += eG * 3; data[j-2] += eB * 3;
                    }
                    data[j] += eR * 5; data[j+1] += eG * 5; data[j+2] += eB * 5;
                    if (x + 1 < w && j + 4 < data.length) {
                        data[j+4] += eR; data[j+5] += eG; data[j+6] += eB;
                    }
                }
            }
        },
        falsefloyd: function(errR, errG, errB, x, y, w, h, data) {
            var eR = errR / 16, eG = errG / 16, eB = errB / 16;
            if (y + 1 < h) {
                var j = ((y + 1) * w + x) * 4;
                if (j < data.length) {
                    data[j] += eR * 8; data[j+1] += eG * 8; data[j+2] += eB * 8;
                    if (x + 1 < w && j + 4 < data.length) {
                        data[j+4] += eR * 8; data[j+5] += eG * 8; data[j+6] += eB * 8;
                    }
                }
            }
        },
        stucki: function(errR, errG, errB, x, y, w, h, data) {
            var eR = errR / 42, eG = errG / 42, eB = errB / 42;
            var i = (y * w + x) * 4;
            if (x + 1 < w && i + 4 < data.length) {
                data[i+4] += eR * 8; data[i+5] += eG * 8; data[i+6] += eB * 8;
            }
            if (x + 2 < w && i + 8 < data.length) {
                data[i+8] += eR * 4; data[i+9] += eG * 4; data[i+10] += eB * 4;
            }
            if (y + 1 < h) {
                var j = ((y + 1) * w + x) * 4;
                if (j < data.length) {
                    if (x > 1 && j - 8 >= 0) {
                        data[j-8] += eR * 2; data[j-7] += eG * 2; data[j-6] += eB * 2;
                    }
                    if (x > 0 && j - 4 >= 0) {
                        data[j-4] += eR * 4; data[j-3] += eG * 4; data[j-2] += eB * 4;
                    }
                    data[j] += eR * 8; data[j+1] += eG * 8; data[j+2] += eB * 8;
                    if (x + 1 < w && j + 4 < data.length) {
                        data[j+4] += eR * 4; data[j+5] += eG * 4; data[j+6] += eB * 4;
                    }
                    if (x + 2 < w && j + 8 < data.length) {
                        data[j+8] += eR * 2; data[j+9] += eG * 2; data[j+10] += eB * 2;
                    }
                }
            }
            if (y + 2 < h) {
                var k = ((y + 2) * w + x) * 4;
                if (k < data.length) {
                    if (x > 1 && k - 8 >= 0) {
                        data[k-8] += eR; data[k-7] += eG; data[k-6] += eB;
                    }
                    if (x > 0 && k - 4 >= 0) {
                        data[k-4] += eR * 2; data[k-3] += eG * 2; data[k-2] += eB * 2;
                    }
                    data[k] += eR * 4; data[k+1] += eG * 4; data[k+2] += eB * 4;
                    if (x + 1 < w && k + 4 < data.length) {
                        data[k+4] += eR * 2; data[k+5] += eG * 2; data[k+6] += eB * 2;
                    }
                    if (x + 2 < w && k + 8 < data.length) {
                        data[k+8] += eR; data[k+9] += eG; data[k+10] += eB;
                    }
                }
            }
        },
        atkinson: function(errR, errG, errB, x, y, w, h, data) {
            var eR = errR / 8, eG = errG / 8, eB = errB / 8;
            var i = (y * w + x) * 4;
            if (x + 1 < w && i + 4 < data.length) {
                data[i+4] += eR; data[i+5] += eG; data[i+6] += eB;
            }
            if (x + 2 < w && i + 8 < data.length) {
                data[i+8] += eR; data[i+9] += eG; data[i+10] += eB;
            }
            if (y + 1 < h) {
                var j = ((y + 1) * w + x) * 4;
                if (j < data.length) {
                    if (x > 0 && j - 4 >= 0) {
                        data[j-4] += eR; data[j-3] += eG; data[j-2] += eB;
                    }
                    data[j] += eR; data[j+1] += eG; data[j+2] += eB;
                    if (x + 1 < w && j + 4 < data.length) {
                        data[j+4] += eR; data[j+5] += eG; data[j+6] += eB;
                    }
                }
            }
            if (y + 2 < h) {
                var k = ((y + 2) * w + x) * 4;
                if (k < data.length) {
                    data[k] += eR; data[k+1] += eG; data[k+2] += eB;
                }
            }
        },
        jarvis: function(errR, errG, errB, x, y, w, h, data) {
            var eR = errR / 48, eG = errG / 48, eB = errB / 48;
            var i = (y * w + x) * 4;
            if (x + 1 < w && i + 4 < data.length) {
                data[i+4] += eR * 7; data[i+5] += eG * 7; data[i+6] += eB * 7;
            }
            if (x + 2 < w && i + 8 < data.length) {
                data[i+8] += eR * 5; data[i+9] += eG * 5; data[i+10] += eB * 5;
            }
            if (y + 1 < h) {
                var j = ((y + 1) * w + x) * 4;
                if (j < data.length) {
                    if (x > 2 && j - 12 >= 0) {
                        data[j-12] += eR * 3; data[j-11] += eG * 3; data[j-10] += eB * 3;
                    }
                    if (x > 1 && j - 8 >= 0) {
                        data[j-8] += eR * 5; data[j-7] += eG * 5; data[j-6] += eB * 5;
                    }
                    if (x > 0 && j - 4 >= 0) {
                        data[j-4] += eR * 7; data[j-3] += eG * 7; data[j-2] += eB * 7;
                    }
                    data[j] += eR * 5; data[j+1] += eG * 5; data[j+2] += eB * 5;
                    if (x + 1 < w && j + 4 < data.length) {
                        data[j+4] += eR * 3; data[j+5] += eG * 3; data[j+6] += eB * 3;
                    }
                    if (x + 2 < w && j + 8 < data.length) {
                        data[j+8] += eR; data[j+9] += eG; data[j+10] += eB;
                    }
                }
            }
            if (y + 2 < h) {
                var k = ((y + 2) * w + x) * 4;
                if (k < data.length) {
                    if (x > 2 && k - 12 >= 0) {
                        data[k-12] += eR; data[k-11] += eG; data[k-10] += eB;
                    }
                    if (x > 1 && k - 8 >= 0) {
                        data[k-8] += eR * 3; data[k-7] += eG * 3; data[k-6] += eB * 3;
                    }
                    if (x > 0 && k - 4 >= 0) {
                        data[k-4] += eR * 5; data[k-3] += eG * 5; data[k-2] += eB * 5;
                    }
                    data[k] += eR * 3; data[k+1] += eG * 3; data[k+2] += eB * 3;
                    if (x + 1 < w && k + 4 < data.length) {
                        data[k+4] += eR; data[k+5] += eG; data[k+6] += eB;
                    }
                }
            }
        },
        burkes: function(errR, errG, errB, x, y, w, h, data) {
            var eR = errR / 32, eG = errG / 32, eB = errB / 32;
            var i = (y * w + x) * 4;
            if (x + 1 < w && i + 4 < data.length) {
                data[i+4] += eR * 8; data[i+5] += eG * 8; data[i+6] += eB * 8;
            }
            if (x + 2 < w && i + 8 < data.length) {
                data[i+8] += eR * 4; data[i+9] += eG * 4; data[i+10] += eB * 4;
            }
            if (y + 1 < h) {
                var j = ((y + 1) * w + x) * 4;
                if (j < data.length) {
                    if (x > 1 && j - 8 >= 0) {
                        data[j-8] += eR * 2; data[j-7] += eG * 2; data[j-6] += eB * 2;
                    }
                    if (x > 0 && j - 4 >= 0) {
                        data[j-4] += eR * 4; data[j-3] += eG * 4; data[j-2] += eB * 4;
                    }
                    data[j] += eR * 8; data[j+1] += eG * 8; data[j+2] += eB * 8;
                    if (x + 1 < w && j + 4 < data.length) {
                        data[j+4] += eR * 4; data[j+5] += eG * 4; data[j+6] += eB * 4;
                    }
                    if (x + 2 < w && j + 8 < data.length) {
                        data[j+8] += eR * 2; data[j+9] += eG * 2; data[j+10] += eB * 2;
                    }
                }
            }
        },
        sierra: function(errR, errG, errB, x, y, w, h, data) {
            var eR = errR / 32, eG = errG / 32, eB = errB / 32;
            var i = (y * w + x) * 4;
            if (x + 1 < w && i + 4 < data.length) {
                data[i+4] += eR * 5; data[i+5] += eG * 5; data[i+6] += eB * 5;
            }
            if (x + 2 < w && i + 8 < data.length) {
                data[i+8] += eR * 3; data[i+9] += eG * 3; data[i+10] += eB * 3;
            }
            if (y + 1 < h) {
                var j = ((y + 1) * w + x) * 4;
                if (j < data.length) {
                    if (x > 1 && j - 8 >= 0) {
                        data[j-8] += eR * 2; data[j-7] += eG * 2; data[j-6] += eB * 2;
                    }
                    if (x > 0 && j - 4 >= 0) {
                        data[j-4] += eR * 4; data[j-3] += eG * 4; data[j-2] += eB * 4;
                    }
                    data[j] += eR * 5; data[j+1] += eG * 5; data[j+2] += eB * 5;
                    if (x + 1 < w && j + 4 < data.length) {
                        data[j+4] += eR * 4; data[j+5] += eG * 4; data[j+6] += eB * 4;
                    }
                    if (x + 2 < w && j + 8 < data.length) {
                        data[j+8] += eR * 2; data[j+9] += eG * 2; data[j+10] += eB * 2;
                    }
                }
            }
            if (y + 2 < h) {
                var k = ((y + 2) * w + x) * 4;
                if (k < data.length) {
                    if (x > 0 && k - 4 >= 0) {
                        data[k-4] += eR * 2; data[k-3] += eG * 2; data[k-2] += eB * 2;
                    }
                    data[k] += eR * 3; data[k+1] += eG * 3; data[k+2] += eB * 3;
                    if (x + 1 < w && k + 4 < data.length) {
                        data[k+4] += eR * 2; data[k+5] += eG * 2; data[k+6] += eB * 2;
                    }
                }
            }
        }
    };

    // ----- SORTING -----
    var SORT_FUNCTIONS = {
        none: function(arr) { return arr; },
        rand: function(arr) {
            var shuffled = arr.slice();
            for (var i = shuffled.length - 1; i > 0; i--) {
                var j = Math.floor(Math.random() * (i + 1));
                var temp = shuffled[i];
                shuffled[i] = shuffled[j];
                shuffled[j] = temp;
            }
            return shuffled;
        },
        colors: function(arr) { var a = arr.slice(); a.sort(function(a, b) { return a.color - b.color; }); return a; },
        vertical: function(arr) { var a = arr.slice(); a.sort(function(a, b) { return a.x - b.x; }); return a; },
        horizontal: function(arr) { var a = arr.slice(); a.sort(function(a, b) { return a.y - b.y; }); return a; },
        topleft: function(arr) { var a = arr.slice(); a.sort(function(a, b) { return (a.x + a.y) - (b.x + b.y); }); return a; },
        grid: function(arr) {
            var rows = {};
            arr.forEach(function(p) {
                if (!rows[p.y]) rows[p.y] = [];
                rows[p.y].push(p);
            });
            var result = [];
            var sortedRows = Object.keys(rows).sort(function(a, b) { return parseInt(a) - parseInt(b); });
            sortedRows.forEach(function(y, idx) {
                var row = rows[y];
                if (idx % 2 === 0) row.sort(function(a, b) { return a.x - b.x; });
                else row.sort(function(a, b) { return b.x - a.x; });
                result = result.concat(row);
            });
            return result;
        },
        circle: function(arr) {
            var a = arr.slice();
            var cx = a.reduce(function(s, p) { return s + p.x; }, 0) / a.length;
            var cy = a.reduce(function(s, p) { return s + p.y; }, 0) / a.length;
            a.sort(function(a, b) {
                return Math.hypot(a.x - cx, a.y - cy) - Math.hypot(b.x - cx, b.y - cy);
            });
            return a;
        }
    };

    // ----- YARDIMCILAR -----
    function nearestPaletteColor(r, g, b) {
        var minDist = Infinity, best = 0;
        for (var i = 0; i < PALETTE.length; i++) {
            var pr = PALETTE[i][0], pg = PALETTE[i][1], pb = PALETTE[i][2];
            var dr = r - pr, dg = g - pg, db = b - pb;
            var dist = dr*dr + dg*dg + db*db;
            if (dist < minDist) { minDist = dist; best = i; }
        }
        return best;
    }

    function getCanvasCoords(clientX, clientY) {
        var canvas = document.querySelector('canvas');
        if (!canvas) return { x: 0, y: 0 };

        var rect = canvas.getBoundingClientRect();
        var scaleX = canvas.width / rect.width;
        var scaleY = canvas.height / rect.height;

        var x = Math.round((clientX - rect.left) * scaleX);
        var y = Math.round((clientY - rect.top) * scaleY);

        x = Math.max(0, Math.min(canvas.width - 1, x));
        y = Math.max(0, Math.min(canvas.height - 1, y));

        return { x: x, y: y };
    }

    // ----- WEBSOCKET -----
    window.wipo = {
        pixelspeed: 17,
        queue: [],
        inprogress: false,
        protect: false,
        tickspeed: 1000,
        order: 'circle',
        dither: null,
        agressive_protection: false,
        pixel_type: 'default',
        ws: null,
        wsReady: false,
        wsReconnectTimer: null
    };

    var wsHookActive = false;

    function hookWebSocket() {
        if (wsHookActive) return;
        wsHookActive = true;

        var unmodifiedWS = window.WebSocket;
        window.WebSocket = function(url, protocols) {
            var socket = new unmodifiedWS(url, protocols);

            if (url && url.includes('pixelplace.io')) {
                console.log('[Wipo] WebSocket intercepted.');

                socket.addEventListener('open', function() {
                    console.log('[Wipo] WebSocket opened.');
                    window.wipo.ws = socket;
                    window.wipo.wsReady = true;
                    if (window.wipo.wsReconnectTimer) {
                        clearTimeout(window.wipo.wsReconnectTimer);
                        window.wipo.wsReconnectTimer = null;
                    }
                });

                socket.addEventListener('close', function() {
                    console.log('[Wipo] WebSocket closed.');
                    window.wipo.wsReady = false;
                    if (!window.wipo.wsReconnectTimer) {
                        window.wipo.wsReconnectTimer = setTimeout(function() {
                            console.log('[Wipo] Attempting to reconnect WebSocket...');
                            hookWebSocket();
                        }, 3000);
                    }
                });

                socket.addEventListener('error', function() {
                    console.log('[Wipo] WebSocket error.');
                    window.wipo.wsReady = false;
                });
            }

            return socket;
        };
        window.WebSocket.prototype = unmodifiedWS.prototype;
    }
    hookWebSocket();

    // ----- BOT SINIFI -----
    function WipoBot() {
        var self = this;
        self.lastPlace = 0;
        self.queue = [];
        self.isRunning = false;
        self.isProcessing = false;
        self.placedCount = 0;
        self.totalPixels = 0;
        self.pixelSpeed = 17;
        self.protectMode = false;
        self.aggressiveProtect = false;
        self.protectedPixels = new Map();
        self.canvasCache = null;
        self.ppsCounter = 0;
        self.ppsInterval = null;
        self.startTime = 0;
        self.priorityAreas = [];
        self.ditherType = 'none';
        self.sortType = 'circle';
        self.pixelType = 'default';
        self.currentImageData = null;
        self.startX = 0;
        self.startY = 0;
        self.diffCheck = false;
        self.protectTimer = null;
        self.isProcessingImage = false;
        self.canvasWidth = 0;
        self.canvasHeight = 0;
        self.totalSent = 0;
        self.totalFailed = 0;
        self.isPaused = false;
    }

    var bot = null;

    WipoBot.prototype.processImage = async function(imageData, startX, startY, ditherType, enableDiff) {
        var self = this;
        var data = imageData.data, width = imageData.width, height = imageData.height;
        var result = [];
        var w = width, h = height;
        var workData = new Uint8ClampedArray(data);
        var ditherFn = DITHER[ditherType] || DITHER.none;
        var BATCH_SIZE = 5000;

        var canvas = document.querySelector('canvas');
        if (canvas) {
            self.canvasWidth = canvas.width;
            self.canvasHeight = canvas.height;
        }

        self.isProcessingImage = true;

        for (var y = 0; y < h; y++) {
            for (var x = 0; x < w; x++) {
                var idx = (y * w + x) * 4;
                if (idx + 3 >= workData.length) continue;

                var r = workData[idx];
                var g = workData[idx + 1];
                var b = workData[idx + 2];
                var a = workData[idx + 3];
                if (a < 128) continue;

                var colorIdx = nearestPaletteColor(r, g, b);
                var pr = PALETTE[colorIdx][0], pg = PALETTE[colorIdx][1], pb = PALETTE[colorIdx][2];

                var errR = r - pr;
                var errG = g - pg;
                var errB = b - pb;

                if (ditherType !== 'none') {
                    ditherFn(errR, errG, errB, x, y, w, h, workData);
                }

                var px = startX + x;
                var py = startY + y;

                if (px < 0 || py < 0 || px >= self.canvasWidth || py >= self.canvasHeight) {
                    continue;
                }

                if (enableDiff && !self.checkPixelDiff(px, py, colorIdx)) continue;
                result.push({ x: px, y: py, color: colorIdx });

                if (result.length % BATCH_SIZE === 0) {
                    await new Promise(function(r) { setTimeout(r, 0); });
                }
            }
        }

        self.isProcessingImage = false;
        console.log('[Wipo] ' + result.length + ' pixels processed.');
        return result;
    };

    WipoBot.prototype.checkPixelDiff = function(x, y, color) {
        var self = this;
        try {
            if (!self.canvasCache) {
                var canvas = document.querySelector('canvas');
                if (!canvas) return true;
                var ctx = canvas.getContext('2d');
                self.canvasCache = ctx.getImageData(0, 0, canvas.width, canvas.height);
            }
            var idx = (y * self.canvasCache.width + x) * 4;
            if (idx + 3 >= self.canvasCache.data.length) return true;

            var pr = PALETTE[color][0], pg = PALETTE[color][1], pb = PALETTE[color][2];
            var dr = self.canvasCache.data[idx] - pr;
            var dg = self.canvasCache.data[idx + 1] - pg;
            var db = self.canvasCache.data[idx + 2] - pb;
            return (dr*dr + dg*dg + db*db) > 10;
        } catch(e) { return true; }
    };

    WipoBot.prototype.updateCache = function() {
        var self = this;
        try {
            var canvas = document.querySelector('canvas');
            if (!canvas) return;
            var ctx = canvas.getContext('2d');
            self.canvasCache = ctx.getImageData(0, 0, canvas.width, canvas.height);
        } catch(e) {}
    };

    // ----- SEND PIXEL (DUZELTILDI - DOGRUDAN wipo.ws KULLANIR) -----
    WipoBot.prototype.sendPixel = async function(x, y, color, type) {
        var self = this;
        type = type || 'default';
        var wipo = window.wipo;

        // DOGRUDAN wipo.ws KONTROL
        if (!wipo.wsReady || !wipo.ws || wipo.ws.readyState !== WebSocket.OPEN) {
            console.log('[Wipo] WebSocket connection lost.');
            self.totalFailed++;
            return false;
        }

        var canvas = document.querySelector('canvas');
        if (canvas) {
            if (x < 0 || y < 0 || x >= canvas.width || y >= canvas.height) {
                console.log('[Wipo] Invalid coordinates: (' + x + ', ' + y + ')');
                self.totalFailed++;
                return false;
            }
        }

        var now = performance.now();
        var elapsed = now - self.lastPlace;
        var waitTime = self.pixelSpeed - elapsed;
        if (waitTime > 0) {
            await new Promise(function(r) { setTimeout(r, waitTime); });
        }

        try {
            var pixelParam = determinePixelType(x, y, color, type);
            wipo.ws.send('42["p",' + pixelParam + ']');
            self.lastPlace = performance.now();
            self.ppsCounter++;
            self.totalSent++;

            if (self.totalSent % 10 === 0) {
                self.updateCache();
            }

            return true;
        } catch(e) {
            console.error('[Wipo] Send error:', e);
            self.totalFailed++;
            return false;
        }
    };

    // ----- START (DUZELTILDI) -----
    WipoBot.prototype.start = async function() {
        var self = this;
        var wipo = window.wipo;

        if (wipo.inprogress) {
            console.log('[Wipo] Already running.');
            return;
        }

        // DOGRUDAN wipo.ws KONTROL
        if (!wipo.wsReady || !wipo.ws || wipo.ws.readyState !== WebSocket.OPEN) {
            console.log('[Wipo] WebSocket not ready, waiting...');
            setTimeout(function() { self.start(); }, 1000);
            return;
        }

        if (!self.currentImageData && self.queue.length === 0) {
            alert('Please load an image or create a square.');
            return;
        }

        if (self.currentImageData) {
            self.updateStatus('Processing image...');
            var processed = await self.processImage(
                self.currentImageData,
                self.startX,
                self.startY,
                self.ditherType,
                self.diffCheck
            );
            var sorted = SORT_FUNCTIONS[self.sortType] ? SORT_FUNCTIONS[self.sortType](processed) : processed;
            self.queue = self.applyPriorityAreas(sorted);
        }

        if (self.queue.length === 0) {
            alert('No pixels to draw.');
            return;
        }

        self.totalPixels = self.queue.length;
        self.placedCount = 0;
        self.totalSent = 0;
        self.totalFailed = 0;
        wipo.inprogress = true;
        self.startTime = Date.now();
        self.ppsCounter = 0;

        self.updateStatus('Drawing started (' + self.queue.length + ' pixels)');
        console.log('[Wipo] Drawing started, total: ' + self.queue.length + ' pixels');

        if (self.ppsInterval) clearInterval(self.ppsInterval);
        self.ppsInterval = setInterval(function() {
            var elapsed = (Date.now() - self.startTime) / 1000;
            var pps = elapsed > 0 ? Math.round(self.ppsCounter / elapsed) : 0;
            var percent = self.totalPixels > 0 ? Math.round((self.placedCount / self.totalPixels) * 100) : 0;
            self.updateStats('PPS: ' + pps + ' | ' + self.placedCount + '/' + self.totalPixels + ' (' + percent + '%) | Failed: ' + self.totalFailed);
        }, 1000);

        self.processQueue();
    };

    WipoBot.prototype.processQueue = async function() {
        var self = this;
        var wipo = window.wipo;

        while (wipo.inprogress && self.queue.length > 0) {
            if (!wipo.inprogress || self.isPaused) {
                await new Promise(function(r) { setTimeout(r, 100); });
                continue;
            }

            // WS kontrol
            if (!wipo.wsReady || !wipo.ws || wipo.ws.readyState !== WebSocket.OPEN) {
                console.log('[Wipo] WebSocket lost, pausing...');
                self.isPaused = true;
                await new Promise(function(r) { setTimeout(r, 2000); });
                self.isPaused = false;
                continue;
            }

            var pixel = self.queue[0];

            if (!self.canvasCache) {
                self.updateCache();
            }

            if (self.canvasCache) {
                var idx = (pixel.y * self.canvasCache.width + pixel.x) * 4;
                if (idx + 3 < self.canvasCache.data.length) {
                    var pr = PALETTE[pixel.color][0], pg = PALETTE[pixel.color][1], pb = PALETTE[pixel.color][2];
                    var dr = self.canvasCache.data[idx] - pr;
                    var dg = self.canvasCache.data[idx + 1] - pg;
                    var db = self.canvasCache.data[idx + 2] - pb;
                    if (dr*dr + dg*dg + db*db < 10 && self.pixelType === 'default') {
                        self.queue.shift();
                        self.placedCount++;
                        continue;
                    }
                }
            }

            var success = await self.sendPixel(pixel.x, pixel.y, pixel.color, self.pixelType);
            if (success) {
                self.queue.shift();
                self.placedCount++;

                if (self.protectMode || self.pixelType === 'protect') {
                    self.protectedPixels.set(pixel.x + ',' + pixel.y, pixel.color);
                }
            } else {
                var retry = 0;
                var retrySuccess = false;
                while (retry < 3 && !retrySuccess) {
                    await new Promise(function(r) { setTimeout(r, 50); });
                    retrySuccess = await self.sendPixel(pixel.x, pixel.y, pixel.color, self.pixelType);
                    retry++;
                }
                if (retrySuccess) {
                    self.queue.shift();
                    self.placedCount++;
                } else {
                    self.queue.shift();
                    console.log('[Wipo] Pixel (' + pixel.x + ',' + pixel.y + ') skipped.');
                }
            }

            var remaining = self.queue.length;
            var percent = self.totalPixels > 0 ? Math.round((self.placedCount / self.totalPixels) * 100) : 0;
            self.updateStatus('Progress: ' + self.placedCount + '/' + self.totalPixels + ' (' + percent + '%) | ' + remaining + ' remaining');

            await new Promise(function(r) { setTimeout(r, 1); });
        }

        if (self.queue.length === 0 && wipo.inprogress) {
            wipo.inprogress = false;
            self.updateStatus('Completed! (' + self.placedCount + ' pixels, ' + self.totalFailed + ' failed)');
            if (self.ppsInterval) clearInterval(self.ppsInterval);
            console.log('[Wipo] Drawing completed. Total: ' + self.totalSent + ', Failed: ' + self.totalFailed);
        }
    };

    WipoBot.prototype.stop = function() {
        var self = this;
        var wipo = window.wipo;
        wipo.inprogress = false;
        self.isPaused = false;
        self.queue = [];
        if (self.ppsInterval) clearInterval(self.ppsInterval);
        self.updateStatus('Stopped.');
        console.log('[Wipo] Drawing stopped.');
    };

    WipoBot.prototype.createSquare = function(x1, y1, x2, y2, colorId) {
        var self = this;
        var q = [];
        var minX = Math.min(x1, x2), maxX = Math.max(x1, x2);
        var minY = Math.min(y1, y2), maxY = Math.max(y1, y2);

        var canvas = document.querySelector('canvas');
        var maxW = canvas ? canvas.width : Infinity;
        var maxH = canvas ? canvas.height : Infinity;

        for (var y = minY; y <= maxY; y++) {
            for (var x = minX; x <= maxX; x++) {
                if (x >= 0 && y >= 0 && x < maxW && y < maxH) {
                    q.push({ x: x, y: y, color: colorId });
                }
            }
        }
        self.queue = q;
        self.updateStatus('Square: ' + q.length + ' pixels (' + x1 + ',' + y1 + ') - (' + x2 + ',' + y2 + ')');

        var wipo = window.wipo;
        if (wipo.inprogress) {
            self.processQueue();
        }
    };

    WipoBot.prototype.applyPriorityAreas = function(queue) {
        var self = this;
        if (self.priorityAreas.length === 0) return queue;
        var priority = [];
        var normal = [];
        for (var i = 0; i < queue.length; i++) {
            var pixel = queue[i];
            var isPriority = false;
            for (var j = 0; j < self.priorityAreas.length; j++) {
                var area = self.priorityAreas[j];
                if (pixel.x >= area.x1 && pixel.x <= area.x2 &&
                    pixel.y >= area.y1 && pixel.y <= area.y2) {
                    isPriority = true;
                    break;
                }
            }
            if (isPriority) priority.push(pixel);
            else normal.push(pixel);
        }
        return priority.concat(normal);
    };

    WipoBot.prototype.startProtect = function() {
        var self = this;
        if (self.protectTimer) clearInterval(self.protectTimer);
        self.protectTimer = setInterval(function() {
            if (!self.protectMode || !window.wipo.inprogress) return;

            self.updateCache();

            if (self.aggressiveProtect) {
                var toRepair = [];
                var iter = self.protectedPixels.entries();
                var entry = iter.next();
                while (!entry.done) {
                    var key = entry.value[0];
                    var color = entry.value[1];
                    var coords = key.split(',').map(Number);
                    var x = coords[0], y = coords[1];
                    if (!self.checkPixelDiff(x, y, color)) {
                        toRepair.push({ x: x, y: y, color: color });
                    }
                    entry = iter.next();
                }
                if (toRepair.length > 0) {
                    self.queue = toRepair.concat(self.queue);
                    self.updateStatus('Protection: ' + toRepair.length + ' pixels added to queue.');
                }
            }
        }, 3000);
    };

    WipoBot.prototype.updateStatus = function(text) {
        var el = document.getElementById('statusText');
        if (el) el.innerText = text;
    };

    WipoBot.prototype.updateStats = function(text) {
        var el = document.getElementById('statsText');
        if (el) el.innerText = text;
    };

    bot = new WipoBot();

    // ----- GORSEL YUKLEME -----
    function loadImage(file) {
        if (!file) return;
        var reader = new FileReader();
        reader.onload = function(e) {
            var img = new Image();
            img.onload = function() {
                var canvas = document.createElement('canvas');
                var ctx = canvas.getContext('2d');
                var width = img.width, height = img.height;
                var maxDim = 500;
                if (width > maxDim || height > maxDim) {
                    var ratio = Math.min(maxDim / width, maxDim / height);
                    width = Math.floor(width * ratio);
                    height = Math.floor(height * ratio);
                }
                canvas.width = width;
                canvas.height = height;
                ctx.drawImage(img, 0, 0, width, height);
                bot.currentImageData = ctx.getImageData(0, 0, width, height);
                updateOverlay();
                bot.updateStatus('Image loaded: ' + width + 'x' + height);
            };
            img.src = e.target.result;
        };
        reader.readAsDataURL(file);
    }

    // ----- OVERLAY -----
    var overlayImg = null;

    function createOverlay() {
        var old = document.getElementById('wipo-overlay');
        if (old) old.remove();
        var div = document.createElement('div');
        div.id = 'wipo-overlay';
        div.style.cssText = 'position:absolute;pointer-events:none;top:0;left:0;width:100%;height:100%;z-index:500;display:none;overflow:hidden';
        overlayImg = document.createElement('img');
        overlayImg.style.cssText = 'position:absolute;opacity:0.35;pointer-events:none;image-rendering:pixelated';
        div.appendChild(overlayImg);
        var container = document.querySelector('#painting-move');
        if (container) {
            container.style.position = 'relative';
            container.appendChild(div);
        } else {
            document.body.appendChild(div);
        }
    }

    function updateOverlay() {
        if (!overlayImg) createOverlay();
        if (!overlayImg || !bot.currentImageData) {
            if (overlayImg) overlayImg.parentElement.style.display = 'none';
            return;
        }
        var sx = parseInt(document.getElementById('startX') ? document.getElementById('startX').value : 0) || 0;
        var sy = parseInt(document.getElementById('startY') ? document.getElementById('startY').value : 0) || 0;
        bot.startX = sx;
        bot.startY = sy;
        var canvas = document.createElement('canvas');
        canvas.width = bot.currentImageData.width;
        canvas.height = bot.currentImageData.height;
        canvas.getContext('2d').putImageData(bot.currentImageData, 0, 0);
        overlayImg.src = canvas.toDataURL();
        overlayImg.style.left = sx + 'px';
        overlayImg.style.top = sy + 'px';
        overlayImg.style.width = bot.currentImageData.width + 'px';
        overlayImg.style.height = bot.currentImageData.height + 'px';
        overlayImg.parentElement.style.display = 'block';
        document.getElementById('overlayToggle').innerHTML = 'Gizle';
    }

    function toggleOverlay() {
        if (!overlayImg) createOverlay();
        var visible = overlayImg.parentElement.style.display !== 'none';
        overlayImg.parentElement.style.display = visible ? 'none' : 'block';
        document.getElementById('overlayToggle').innerHTML = visible ? 'Goster' : 'Gizle';
        if (!visible) updateOverlay();
    }

    // ----- MOUSE KOORDINAT -----
    window._wipoMouseX = 0;
    window._wipoMouseY = 0;

    // ----- MENU VE GUI -----
    function createUI() {
        GM_addStyle(`
            @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
            :root { --gui-main-color: #7300ff; }

            #wipo-menu-btn {
                position:fixed;left:10px;top:50%;transform:translateY(-50%);
                background:#1a1f2e;border:2px solid #7300ff;border-radius:8px;
                padding:12px 6px;color:#eaeef5;z-index:10001;cursor:pointer;
                font-family:'Inter',sans-serif;font-size:14px;font-weight:700;
                writing-mode:vertical-lr;letter-spacing:2px;
                box-shadow:0 4px 20px rgba(0,0,0,0.8);
                transition:all 0.3s ease;user-select:none;
                background:linear-gradient(180deg,#1a1f2e,#0d111c);
            }
            #wipo-menu-btn:hover { transform:translateY(-50%) scale(1.05); background:#2a2f3e; }

            #wipo-panel {
                position:fixed;left:60px;top:50%;transform:translateY(-50%);
                background:#141a28;border:1px solid #2f3a55;border-radius:20px;
                padding:0;color:#eaeef5;font-family:'Inter',sans-serif;
                width:460px;max-height:85vh;overflow:hidden;
                backdrop-filter:blur(12px);box-shadow:0 16px 40px rgba(0,0,0,0.8);
                z-index:9999;font-size:13px;transition:0.3s ease;
                display:flex;flex-direction:column;
            }
            #wipo-panel.hidden { display:none; }

            .wipo-header {
                padding:12px 20px 8px 20px;border-bottom:1px solid #1f2840;
                display:flex;justify-content:space-between;align-items:center;
                cursor:move;flex-shrink:0;background:#0d111c;
                border-radius:20px 20px 0 0;
            }
            .wipo-header .title { font-weight:700;font-size:18px;color:var(--gui-main-color);font-family:Verdana,Tahoma,sans-serif; }
            .wipo-header .version { font-weight:300;color:#5d6a84;font-size:11px;font-family:Verdana,Tahoma,sans-serif; }

            .wipo-tabs {
                display:flex;padding:0 16px;gap:4px;border-bottom:1px solid #1a1f2e;
                flex-shrink:0;background:#0d111c;
            }
            .wipo-tab-btn {
                padding:8px 16px;font-size:12px;font-weight:500;color:#5d6a84;
                cursor:pointer;border:none;background:transparent;
                border-radius:8px 8px 0 0;transition:0.2s;font-family:'Inter',sans-serif;
            }
            .wipo-tab-btn:hover { color:#c8d0e5;background:#1a1f2e; }
            .wipo-tab-btn.active { color:var(--gui-main-color);background:#141a28; }

            .wipo-body { padding:16px 20px;overflow-y:auto;flex:1;background:#141a28; }
            .wipo-body::-webkit-scrollbar { width:4px; }
            .wipo-body::-webkit-scrollbar-thumb { background:#3a4a6a;border-radius:10px; }

            .wipo-tab-content { display:none;flex-direction:column;gap:10px; }
            .wipo-tab-content.active { display:flex; }

            .wipo-row { display:flex;gap:8px;flex-wrap:wrap;align-items:center; }
            .wipo-row label { font-size:11px;color:#a5b1cc;min-width:40px;font-weight:500;font-family:'Inter',sans-serif; }
            .wipo-row input,.wipo-row select {
                background:#0b101c;border:1px solid #2a3450;border-radius:30px;
                padding:6px 12px;color:white;font-size:12px;flex:1;min-width:40px;
                font-family:'Inter',sans-serif;
            }
            .wipo-row input:focus,.wipo-row select:focus { outline:none;border-color:var(--gui-main-color);box-shadow:0 0 0 2px rgba(115,0,255,0.2); }
            .wipo-row input[type="file"] { padding:4px 8px;background:#111a2a;flex:2;cursor:pointer; }
            .wipo-row input[type="file"]::file-selector-button {
                background:#2d3a55;border:none;border-radius:30px;padding:4px 16px;
                color:white;font-weight:500;cursor:pointer;font-family:'Inter',sans-serif;
                transition:0.2s;
            }
            .wipo-row input[type="file"]::file-selector-button:hover { background:#3f5280; }
            .wipo-row input[type="checkbox"] { flex:0;min-width:auto;width:16px;height:16px;accent-color:var(--gui-main-color); }
            .wipo-row button {
                background:#2d3d62;border:none;border-radius:30px;padding:6px 16px;
                color:white;font-weight:600;cursor:pointer;transition:0.15s;
                flex:1;font-size:12px;font-family:'Inter',sans-serif;
            }
            .wipo-row button:hover { background:#3f5280;transform:scale(0.97); }
            .wipo-row button:active { transform:scale(0.95); }
            .wipo-row button.danger { background:#6d2e3a; }
            .wipo-row button.danger:hover { background:#8f3b4a; }
            .wipo-row button.success { background:#1f6d4a; }
            .wipo-row button.success:hover { background:#2a8a5e; }
            .wipo-row button.warning { background:#8a6d2a; }
            .wipo-row button.warning:hover { background:#aa8830; }
            .wipo-row button.purple { background:#4a2d7a; }
            .wipo-row button.purple:hover { background:#5d3a9a; }
            .wipo-row .small-btn { flex:0.4;padding:4px 10px;font-size:10px; }

            .status {
                background:#0b111e;padding:6px 14px;border-radius:30px;
                font-size:11px;border:1px solid #232d44;color:#c8d0e5;
                overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
                font-family:'Inter',sans-serif;
            }
            .footer {
                font-size:9px;color:#455470;text-align:right;padding:8px 20px;
                border-top:1px solid #1a1f2e;flex-shrink:0;font-family:'Inter',sans-serif;
                background:#0d111c;border-radius:0 0 20px 20px;
            }
            #inspectorText { font-size:10px;color:#88aacc; }
            .wipo-section-title {
                font-size:11px;font-weight:600;color:#5d6a84;
                text-transform:uppercase;letter-spacing:0.5px;
                margin:8px 0 4px 0;border-bottom:1px solid #1a1f2e;padding-bottom:4px;
            }
            #wipo-overlay { pointer-events:none; }
            .wipo-row .flex-2 { flex:2; }
            .wipo-row .flex-3 { flex:3; }
        `);

        // Menu Button
        var menuBtn = document.createElement('div');
        menuBtn.id = 'wipo-menu-btn';
        menuBtn.textContent = 'WIPO';
        menuBtn.title = 'Wipo Bot Show/Hide';
        document.body.appendChild(menuBtn);

        // Panel
        var panel = document.createElement('div');
        panel.id = 'wipo-panel';
        panel.innerHTML = `
            <div class="wipo-header" id="wipo-drag">
                <span class="title">WIPO</span>
                <span class="version">v2.0.3</span>
            </div>
            <div class="wipo-tabs">
                <button class="wipo-tab-btn active" data-tab="botting">Botting</button>
                <button class="wipo-tab-btn" data-tab="settings">Settings</button>
            </div>
            <div class="wipo-body">
                <div class="wipo-tab-content active" id="tab-botting">
                    <div class="wipo-row">
                        <label>Start X</label>
                        <input type="number" id="startX" value="0" step="1" />
                        <label>Y</label>
                        <input type="number" id="startY" value="0" step="1" />
                        <button id="overlayToggle" class="small-btn purple">Show</button>
                    </div>
                    <div class="wipo-row">
                        <label>Speed</label>
                        <input type="number" id="speedInput" value="17" step="1" min="14" max="21" style="flex:0.5;" />
                        <span style="font-size:10px;color:#5d6a84;">14-21</span>
                        <label><input type="checkbox" id="diffCheck" /> Diff</label>
                    </div>
                    <div class="wipo-row">
                        <label>Image</label>
                        <input type="file" id="imageUpload" accept="image/*" />
                    </div>
                    <div class="wipo-row">
                        <label>Dither</label>
                        <select id="ditherSelect">
                            <option value="none">None</option>
                            <option value="floyd">Floyd-Steinberg</option>
                            <option value="falsefloyd">False Floyd</option>
                            <option value="stucki">Stucki</option>
                            <option value="atkinson">Atkinson</option>
                            <option value="jarvis">Jarvis</option>
                            <option value="burkes">Burkes</option>
                            <option value="sierra">Sierra</option>
                        </select>
                    </div>
                    <div class="wipo-row">
                        <label>Sort</label>
                        <select id="sortSelect">
                            <option value="none">None</option>
                            <option value="grid">Grid</option>
                            <option value="topleft">Top Left</option>
                            <option value="rand">Random</option>
                            <option value="colors">Colors</option>
                            <option value="vertical">Vertical</option>
                            <option value="horizontal">Horizontal</option>
                            <option value="circle">Circle</option>
                        </select>
                    </div>
                    <div class="wipo-row">
                        <label>Color ID</label>
                        <input type="number" id="squareColor" value="0" min="0" max="63" style="flex:0.5;" />
                        <button id="squareBtn" class="success small-btn">Square</button>
                        <button id="priorityBtn" class="warning small-btn">Priority</button>
                    </div>
                    <div class="wipo-row">
                        <label>Pixel Type</label>
                        <select id="pixelType" style="flex:0.8;">
                            <option value="default">Default</option>
                            <option value="protect">Protect</option>
                            <option value="unprotect">Unprotect</option>
                            <option value="replace">Replace</option>
                        </select>
                    </div>
                    <div class="wipo-row">
                        <button id="startBtn" class="success">Start</button>
                        <button id="stopBtn" class="danger">Stop</button>
                        <button id="protectBtn">Protect</button>
                        <button id="inspectorBtn">Inspect</button>
                    </div>
                    <div class="status" id="statusText">Ready</div>
                    <div class="status" id="statsText">PPS: 0 | 0/0 (0%)</div>
                    <div class="status" id="inspectorText">Hover over pixel</div>
                </div>
                <div class="wipo-tab-content" id="tab-settings">
                    <div class="wipo-section-title">Appearance</div>
                    <div class="wipo-row">
                        <label>Color</label>
                        <input type="color" id="themeColor" value="#7300ff" style="flex:0;width:50px;height:30px;padding:2px;" />
                        <button id="applyColorBtn" class="small-btn">Apply</button>
                    </div>
                    <div class="wipo-section-title">Data</div>
                    <div class="wipo-row">
                        <button id="clearProgressBtn" class="danger small-btn">Clear Progress</button>
                    </div>
                    <div class="footer" style="border:none;padding:8px 0 0 0;text-align:center;">
                        Hotkeys: B (coord) | X (square) | Alt+W (stop)
                    </div>
                </div>
            </div>
            <div class="footer">Wipo v2.0.3 · Made By Kiwe</div>
        `;
        document.body.appendChild(panel);

        // Drag
        var dragHandle = document.getElementById('wipo-drag');
        var isDragging = false, offsetX, offsetY;
        dragHandle.addEventListener('mousedown', function(e) {
            isDragging = true;
            var rect = panel.getBoundingClientRect();
            offsetX = e.clientX - rect.left;
            offsetY = e.clientY - rect.top;
            panel.style.transition = 'none';
        });
        document.addEventListener('mousemove', function(e) {
            if (!isDragging) return;
            panel.style.left = (e.clientX - offsetX) + 'px';
            panel.style.top = (e.clientY - offsetY) + 'px';
            panel.style.transform = 'none';
        });
        document.addEventListener('mouseup', function() {
            isDragging = false;
            panel.style.transition = '0.3s ease';
        });

        // Menu toggle
        menuBtn.addEventListener('click', function() {
            panel.classList.toggle('hidden');
            menuBtn.style.borderColor = panel.classList.contains('hidden') ? '#ff4444' : '#7300ff';
        });

        // Tab switching
        document.querySelectorAll('.wipo-tab-btn').forEach(function(btn) {
            btn.addEventListener('click', function() {
                document.querySelectorAll('.wipo-tab-content').forEach(function(el) { el.classList.remove('active'); });
                document.querySelectorAll('.wipo-tab-btn').forEach(function(b) { b.classList.remove('active'); });
                document.getElementById('tab-' + this.dataset.tab).classList.add('active');
                this.classList.add('active');
            });
        });

        // Overlay
        createOverlay();
        document.getElementById('overlayToggle').addEventListener('click', toggleOverlay);

        // Input changes
        document.getElementById('startX').addEventListener('change', updateOverlay);
        document.getElementById('startY').addEventListener('change', updateOverlay);

        // Speed
        document.getElementById('speedInput').addEventListener('input', function() {
            window.wipo.pixelspeed = Math.min(21, Math.max(14, parseInt(this.value) || 17));
            bot.pixelSpeed = window.wipo.pixelspeed;
        });

        // Settings to bot
        document.getElementById('ditherSelect').addEventListener('change', function() {
            bot.ditherType = this.value;
        });
        document.getElementById('sortSelect').addEventListener('change', function() {
            bot.sortType = this.value;
        });
        document.getElementById('pixelType').addEventListener('change', function() {
            bot.pixelType = this.value;
            window.wipo.pixel_type = this.value;
        });
        document.getElementById('diffCheck').addEventListener('change', function() {
            bot.diffCheck = this.checked;
        });

        // Start
        document.getElementById('startBtn').addEventListener('click', function() {
            bot.startX = parseInt(document.getElementById('startX').value) || 0;
            bot.startY = parseInt(document.getElementById('startY').value) || 0;
            bot.pixelSpeed = window.wipo.pixelspeed;
            bot.ditherType = document.getElementById('ditherSelect').value;
            bot.sortType = document.getElementById('sortSelect').value;
            bot.pixelType = document.getElementById('pixelType').value;
            bot.diffCheck = document.getElementById('diffCheck').checked;
            bot.start();
        });

        // Stop
        document.getElementById('stopBtn').addEventListener('click', function() {
            bot.stop();
        });

        // Image upload
        document.getElementById('imageUpload').addEventListener('change', function(e) {
            if (e.target.files[0]) loadImage(e.target.files[0]);
        });

        // Square
        document.getElementById('squareBtn').addEventListener('click', function() {
            var x1 = parseInt(document.getElementById('startX').value) || 0;
            var y1 = parseInt(document.getElementById('startY').value) || 0;
            var colorId = parseInt(document.getElementById('squareColor').value) || 0;
            var mx = window._wipoMouseX || 0;
            var my = window._wipoMouseY || 0;
            var coords = getCanvasCoords(mx, my);
            bot.createSquare(x1, y1, coords.x, coords.y, colorId);
        });

        // Priority
        document.getElementById('priorityBtn').addEventListener('click', function() {
            var x1 = parseInt(prompt('Priority area X1:')) || 0;
            var y1 = parseInt(prompt('Priority area Y1:')) || 0;
            var x2 = parseInt(prompt('Priority area X2:')) || 100;
            var y2 = parseInt(prompt('Priority area Y2:')) || 100;
            if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) {
                alert('Invalid coordinates. Please enter numbers.');
                return;
            }
            bot.priorityAreas.push({ x1: x1, y1: y1, x2: x2, y2: y2 });
            bot.updateStatus('Priority area added: (' + x1 + ',' + y1 + ') - (' + x2 + ',' + y2 + ')');
        });

        // Protect
        document.getElementById('protectBtn').addEventListener('click', function() {
            bot.protectMode = !bot.protectMode;
            window.wipo.protect = bot.protectMode;
            if (bot.protectMode) {
                bot.aggressiveProtect = confirm('Enable aggressive protection?');
                window.wipo.agressive_protection = bot.aggressiveProtect;
                bot.startProtect();
                bot.updateStatus('Protection active');
            } else {
                if (bot.protectTimer) clearInterval(bot.protectTimer);
                bot.updateStatus('Protection inactive');
            }
        });

        // Inspect
        var inspectorActive = false;
        document.getElementById('inspectorBtn').addEventListener('click', function() {
            inspectorActive = !inspectorActive;
            document.getElementById('inspectorText').innerText = inspectorActive ?
                'Inspection active (hover over pixel)' : 'Inspection inactive';
        });

        document.addEventListener('mousemove', function(e) {
            window._wipoMouseX = e.clientX;
            window._wipoMouseY = e.clientY;

            if (!inspectorActive) return;
            var coords = getCanvasCoords(e.clientX, e.clientY);
            var canvas = document.querySelector('canvas');
            if (!canvas || coords.x < 0 || coords.y < 0 || coords.x >= canvas.width || coords.y >= canvas.height) return;
            try {
                var ctx = canvas.getContext('2d');
                var pixel = ctx.getImageData(coords.x, coords.y, 1, 1);
                document.getElementById('inspectorText').innerText =
                    'Pixel (' + coords.x + ',' + coords.y + ') RGB(' + pixel.data[0] + ',' + pixel.data[1] + ',' + pixel.data[2] + ')';
            } catch(e) {}
        });

        // Color
        document.getElementById('applyColorBtn').addEventListener('click', function() {
            var color = document.getElementById('themeColor').value;
            document.querySelector(':root').style.setProperty('--gui-main-color', color);
            document.getElementById('wipo-menu-btn').style.borderColor = color;
        });

        // Clear
        document.getElementById('clearProgressBtn').addEventListener('click', function() {
            if (confirm('Clear progress data?')) {
                GM_setValue('wipo_progress', '');
                bot.updateStatus('Progress cleared.');
            }
        });

        // Hotkeys
        document.addEventListener('keydown', function(e) {
            if (e.key === 'b' || e.key === 'B') {
                var mx = window._wipoMouseX || 0;
                var my = window._wipoMouseY || 0;
                var coords = getCanvasCoords(mx, my);
                document.getElementById('startX').value = coords.x;
                document.getElementById('startY').value = coords.y;
                updateOverlay();
                bot.updateStatus('Coordinates: ' + coords.x + ', ' + coords.y);
            }
            if (e.key === 'x' || e.key === 'X') {
                var x1 = parseInt(document.getElementById('startX').value) || 0;
                var y1 = parseInt(document.getElementById('startY').value) || 0;
                var mx = window._wipoMouseX || 0;
                var my = window._wipoMouseY || 0;
                var coords = getCanvasCoords(mx, my);
                var colorId = parseInt(document.getElementById('squareColor').value) || 0;
                bot.createSquare(x1, y1, coords.x, coords.y, colorId);
            }
            if (e.key === 'w' && e.altKey) {
                bot.stop();
                bot.updateStatus('All processes stopped.');
                e.preventDefault();
            }
        });

        // WebSocket kontrol
        setTimeout(function() {
            if (window.wipo.ws && window.wipo.ws.readyState === WebSocket.OPEN) {
                window.wipo.wsReady = true;
                console.log('[Wipo] WebSocket ready.');
            } else {
                console.log('[Wipo] WebSocket not ready, waiting...');
            }
        }, 2000);

        console.log('[Wipo] GUI created.');
    }

    // ----- BASLAT -----
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', function() {
            if (window.location.hostname === 'pixelplace.io') createUI();
        });
    } else {
        if (window.location.hostname === 'pixelplace.io') setTimeout(createUI, 100);
    }

})();