QuickCoords

A compact tool for saving coordinates and instantly jumping to them on PixelPlanet.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name         QuickCoords
// @name:ru      БыстрыеКорды
// @namespace    http://tampermonkey.net/
// @version      2.5.3
// @description  A compact tool for saving coordinates and instantly jumping to them on PixelPlanet.
// @description:ru Компактный инструмент для сохранения координат и мгновенного перехода к ним на PixelPlanet.
// @author       Flips
// @match        https://pixelplanet.fun/*
// @match        https://fuckyouarkeros.fun/*
// @match        https://pixeldays.ru/*
// @match        https://pixeldays.xyz/*
// @icon         https://images.icon-icons.com/2248/PNG/512/message_arrow_right_icon_136413.png
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    let wasDragging = false;
    const uiContainer = document.querySelector("#ui") || document.querySelector("#root") || document.body;
    const PANEL_TRANSITION = "width 0.2s ease, height 0.2s ease, padding 0.2s ease";
    const CANVAS_NAMES = {
        d: "Earth",
        s: "Minimap",
        m: "Moon",
        v: "3D Canvas",
        c: "Coronavirus",
        t: "Top10",
        l: "2bit",
        w: "1bit",
        y: "PixelZone",
        z: "PixelCanvas"
    };
    // ──────────────────────────────────────────────
    // Тема панели
    // ──────────────────────────────────────────────
    const THEMES = {
        light: {
            bg: "rgba(230,230,230,0.85)",
            color: "black",
            border: "1px solid black",
            inputBg: "white",
            inputColor: "black",
            inputBorder: "1px solid #888",
            btnBg: "#eee",
            btnColor: "black",
            btnBorder: "1px solid #888",
            listBg: "rgba(255,255,255,0.4)",
            emojiBtnBg: "white",
            emojiBtnBorder: "1px solid #888"
        },
        dark: {
            bg: "rgba(51, 51, 51, 0.88)",
            color: "#e0e0e0",
            border: "1px solid #000000",
            inputBg: "#2a2a38",
            inputColor: "#e0e0e0",
            inputBorder: "1px solid #555",
            btnBg: "#3a3a4a",
            btnColor: "#e0e0e0",
            btnBorder: "1px solid #555",
            listBg: "rgba(20,20,30,0.6)",
            emojiBtnBg: "#333333",
            emojiBtnBorder: "1px solid #555"
        }
    };
    let currentTheme = localStorage.getItem("quickcoords_theme") || "light";
    const styleId = "quickcoords-theme-styles";
    let styleEl = document.getElementById(styleId);
    if (!styleEl) {
        styleEl = document.createElement("style");
        styleEl.id = styleId;
        document.head.appendChild(styleEl);
    }
    function applyTheme(themeName) {
        const th = THEMES[themeName];
        if (!th) return;
        panel.style.background = th.bg;
        panel.style.color = th.color;
        panel.style.border = th.border;
        const nameInput = document.getElementById("qc_name");
        if (nameInput) {
            nameInput.style.background = th.inputBg;
            nameInput.style.color = th.inputColor;
            nameInput.style.border = th.inputBorder;
        }
        const saveBtn = document.getElementById("qc_save");
        if (saveBtn) {
            saveBtn.style.background = th.btnBg;
            saveBtn.style.color = th.btnColor;
            saveBtn.style.border = th.btnBorder;
        }
        const list = document.getElementById("qc_list");
        if (list) {
            list.style.background = th.listBg;
        }
        styleEl.textContent = `
            #qc_list button,
            #qc_list button[data-go],
            #qc_list button[data-copy],
            #qc_list button[data-del],
            #qc_list button.emoji-btn {
                background: ${th.btnBg};
                color: ${th.btnColor};
                border: ${th.btnBorder};
            }
            #qc_list button[data-copy] {
                background: ${themeName === "dark" ? "#555" : "#666"};
            }
            #qc_list button[data-del] {
                background: ${themeName === "dark" ? "#733" : "#a33"};
            }
            #qc_list button.emoji-btn {
                background: ${th.emojiBtnBg};
                border: ${th.emojiBtnBorder};
            }
            #qc_version {
                font-size: 11px;
                opacity: 0.7;
                margin-left: 8px;
                pointer-events: none;
                color: inherit;
            }
        `;
    }
    // ──────────────────────────────────────────────
    // Создание панели
    // ──────────────────────────────────────────────
    const panel = document.createElement("div");
    panel.id = "quickcoords_panel";
    panel.style.position = "fixed";
    panel.style.padding = "10px";
    panel.style.borderRadius = "6px";
    panel.style.zIndex = "1";
    panel.style.fontSize = "14px";
    panel.style.width = "260px";
    panel.style.userSelect = "none";
    panel.style.resize = "none";
    panel.style.overflow = "hidden";
    panel.style.transition = PANEL_TRANSITION;
    panel.innerHTML = `
        <div id="qc_header" style="display:flex; align-items:center; justify-content:space-between; cursor:pointer; white-space:nowrap; position:relative;">
            <div style="display:flex; align-items:center;">
                <span id="qc_title">📍 QuickCoords</span>
                <span id="qc_version">by Flips v2.5.3</span>
            </div>
            <button id="qc_theme_toggle" title="Переключить тему" style="
                background: rgba(0,0,0,0.25);
                color: inherit;
                border: none;
                border-radius: 4px;
                width: 28px;
                height: 28px;
                font-size: 16px;
                cursor: pointer;
                display: flex;
                align-items: center;
                justify-content: center;
                margin-left: auto;
            ">🌙</button>
        </div>
        <div id="qc_content" style="opacity:1; transition: opacity 0.2s ease;">
            <input id="qc_name" placeholder="Name" style="width:100%; margin-top:5px;">
            <button id="qc_save" style="width:100%; margin-top:5px;">Save</button>
            <div id="qc_list" style="margin-top:10px; max-height:250px; overflow-y:auto;"></div>
        </div>
        <div id="qc_resize" style="
            width:16px;
            height:16px;
            position:absolute;
            right:2px;
            bottom:2px;
            cursor:se-resize;
            background:
                linear-gradient(135deg, transparent 0%, transparent 40%, #888 40%, #888 50%, transparent 50%) no-repeat,
                linear-gradient(135deg, transparent 0%, transparent 60%, #888 60%, #888 70%, transparent 70%) no-repeat,
                linear-gradient(135deg, transparent 0%, transparent 80%, #888 80%, #888 90%, transparent 90%) no-repeat;
            background-size: 100% 100%;
        "></div>
    `;
    uiContainer.appendChild(panel);
    applyTheme(currentTheme);
    document.getElementById("qc_theme_toggle").innerText = currentTheme === "dark" ? "☀️" : "🌙";
    // ──────────────────────────────────────────────
    // Остальные функции (без изменений)
    // ──────────────────────────────────────────────
    const savedPos = JSON.parse(localStorage.getItem("quick_coords_panel_pos") || "{}");
    panel.style.top = savedPos.top || "100px";
    panel.style.left = savedPos.left || "10px";
    const savedSize = JSON.parse(localStorage.getItem("quick_coords_panel_size") || "{}");
    if (savedSize.width) panel.style.width = savedSize.width;
    if (savedSize.height) panel.style.height = savedSize.height;
    const savedState = localStorage.getItem("quick_coords_panel_state") || "show";
    if (savedState === "hide") collapsePanelInstant();
    function collapsePanelInstant() {
        const content = document.getElementById("qc_content");
        content.style.display = "none";
        content.style.opacity = "0";
        panel.style.width = "37px";
        panel.style.height = "37px";
        panel.style.padding = "0px";
        const header = document.getElementById("qc_header");
        header.style.width = "37px";
        header.style.height = "37px";
        header.style.justifyContent = "center";
        document.getElementById("qc_title").innerText = "📍";
        document.getElementById("qc_version").style.display = "none";
        document.getElementById("qc_resize").style.display = "none";
        document.getElementById("qc_theme_toggle").style.display = "none";
    }
    function collapsePanel() {
        const content = document.getElementById("qc_content");
        content.style.opacity = "0";
        setTimeout(() => {
            content.style.display = "none";
        }, 200);
        panel.style.width = "37px";
        panel.style.height = "37px";
        panel.style.padding = "0px";
        const header = document.getElementById("qc_header");
        header.style.width = "37px";
        header.style.height = "37px";
        header.style.justifyContent = "center";
        document.getElementById("qc_title").innerText = "📍";
        document.getElementById("qc_version").style.display = "none";
        document.getElementById("qc_resize").style.display = "none";
        document.getElementById("qc_theme_toggle").style.display = "none";
    }
    function expandPanel() {
        const content = document.getElementById("qc_content");
        content.style.display = "block";
        content.style.opacity = "0";
        requestAnimationFrame(() => {
            content.style.opacity = "1";
        });
        const size = JSON.parse(localStorage.getItem("quick_coords_panel_size") || "{}");
        if (size.width) panel.style.width = size.width;
        if (size.height) panel.style.height = size.height;
        panel.style.padding = "10px";
        const header = document.getElementById("qc_header");
        header.style.width = "100%";
        header.style.height = "auto";
        header.style.justifyContent = "space-between";
        document.getElementById("qc_title").innerText = "📍 QuickCoords";
        document.getElementById("qc_version").style.display = "inline";
        document.getElementById("qc_resize").style.display = "block";
        document.getElementById("qc_theme_toggle").style.display = "flex";
    }
    document.getElementById("qc_theme_toggle").onclick = (e) => {
        e.stopPropagation();
        currentTheme = currentTheme === "light" ? "dark" : "light";
        localStorage.setItem("quickcoords_theme", currentTheme);
        applyTheme(currentTheme);
        document.getElementById("qc_theme_toggle").innerText = currentTheme === "dark" ? "☀️" : "🌙";
        updateList();
    };
    let saved = JSON.parse(localStorage.getItem("quick_coords") || "{}");
    const EMOJIS = ["🏠","⭐","🎯","🧱","📍","🔥","⚔️","🛡️","🚩"];
    let openEmojiPopup = null;
    function getCurrentCoords() {
        let hash = window.location.hash.replace('#', '');
        let parts = hash.split(',');
        let canvas = parts[0] || "d";
        if (parts.length < 4) return { canvas, x: 0, y: 0, zoom: 42 };
        return {
            canvas,
            x: parseInt(parts[1]),
            y: parseInt(parts[2]),
            zoom: parseInt(parts[3])
        };
    }
    function createEmojiPopup(targetButton, name) {
        if (openEmojiPopup) openEmojiPopup.remove();
        const th = THEMES[currentTheme];
        const popup = document.createElement("div");
        popup.style.position = "absolute";
        popup.style.background = currentTheme === "dark" ? "#2a2a38" : "white";
        popup.style.color = currentTheme === "dark" ? "#e0e0e0" : "black";
        popup.style.border = th.emojiBtnBorder;
        popup.style.padding = "5px";
        popup.style.borderRadius = "6px";
        popup.style.display = "grid";
        popup.style.gridTemplateColumns = "repeat(3, 32px)";
        popup.style.gridGap = "4px";
        popup.style.zIndex = "9999";
        EMOJIS.forEach(e => {
            const btn = document.createElement("div");
            btn.innerText = e;
            btn.style.width = "32px";
            btn.style.height = "32px";
            btn.style.display = "flex";
            btn.style.alignItems = "center";
            btn.style.justifyContent = "center";
            btn.style.fontSize = "20px";
            btn.style.border = th.emojiBtnBorder;
            btn.style.borderRadius = "6px";
            btn.style.background = th.emojiBtnBg;
            btn.style.cursor = "pointer";
            btn.onclick = () => {
                saved[name].emoji = e;
                localStorage.setItem("quick_coords", JSON.stringify(saved));
                updateList();
                popup.remove();
                openEmojiPopup = null;
            };
            popup.appendChild(btn);
        });
        document.body.appendChild(popup);
        const rect = targetButton.getBoundingClientRect();
        popup.style.left = rect.left + "px";
        popup.style.top = (rect.bottom + 4) + "px";
        openEmojiPopup = popup;
    }
    function updateList() {
        const list = document.getElementById("qc_list");
        list.innerHTML = "";
        for (let name in saved) {
            const { canvas, x, y, zoom, emoji } = saved[name];
            const link = `https://${location.host}/#${canvas},${x},${y},${zoom}`;
            const wrapper = document.createElement("div");
            wrapper.style.marginBottom = "10px";
            const label = document.createElement("div");
            label.innerText = CANVAS_NAMES[canvas] || canvas;
            label.style.fontSize = "10px";
            label.style.opacity = "0.7";
            label.style.marginLeft = "4px";
            label.style.marginBottom = "2px";
            const item = document.createElement("div");
            item.style.display = "flex";
            item.style.alignItems = "center";
            item.style.gap = "4px";
            const emojiBtn = document.createElement("button");
            emojiBtn.innerText = emoji || "?";
            emojiBtn.className = "emoji-btn";
            emojiBtn.style.width = "32px";
            emojiBtn.style.height = "32px";
            emojiBtn.style.display = "flex";
            emojiBtn.style.alignItems = "center";
            emojiBtn.style.justifyContent = "center";
            emojiBtn.style.fontSize = "20px";
            emojiBtn.style.padding = "0";
            emojiBtn.style.borderRadius = "6px";
            emojiBtn.style.cursor = "pointer";
            emojiBtn.onclick = (e) => {
                e.stopPropagation();
                if (openEmojiPopup) {
                    openEmojiPopup.remove();
                    openEmojiPopup = null;
                    return;
                }
                createEmojiPopup(emojiBtn, name);
            };
            const nameBtn = document.createElement("button");
            nameBtn.innerText = name;
            nameBtn.style.flex = "1 1 auto";
            nameBtn.style.minWidth = "0";
            nameBtn.style.overflow = "hidden";
            nameBtn.style.whiteSpace = "nowrap";
            nameBtn.style.textOverflow = "ellipsis";
            nameBtn.setAttribute("data-go", name);
            const copyBtn = document.createElement("button");
            copyBtn.innerText = "Copy";
            copyBtn.style.flex = "0 0 40%";
            copyBtn.setAttribute("data-copy", link);
            const delBtn = document.createElement("button");
            delBtn.innerText = "✖";
            delBtn.style.flex = "0 0 20%";
            delBtn.setAttribute("data-del", name);
            item.appendChild(emojiBtn);
            item.appendChild(nameBtn);
            item.appendChild(copyBtn);
            item.appendChild(delBtn);
            wrapper.appendChild(label);
            wrapper.appendChild(item);
            list.appendChild(wrapper);
        }
    }
    updateList();
    document.addEventListener("click", (e) => {
        if (openEmojiPopup && !openEmojiPopup.contains(e.target)) {
            openEmojiPopup.remove();
            openEmojiPopup = null;
        }
    });
    document.getElementById("qc_save").onclick = () => {
        const name = document.getElementById("qc_name").value.trim();
        if (!name) return alert("Enter a name");
        const { canvas, x, y, zoom } = getCurrentCoords();
        saved[name] = { canvas, x, y, zoom, emoji: null };
        localStorage.setItem("quick_coords", JSON.stringify(saved));
        updateList();
    };
    function jumpTo(canvas, x, y, zoom) {
        window.location.hash = `#${canvas},0,0,1`;
        setTimeout(() => {
            window.location.hash = `#${canvas},${x},${y},${zoom}`;
        }, 50);
    }
    document.getElementById("qc_list").onclick = (e) => {
        const go = e.target.getAttribute("data-go");
        const del = e.target.getAttribute("data-del");
        const copy = e.target.getAttribute("data-copy");
        if (go) {
            const { canvas, x, y, zoom } = saved[go];
            jumpTo(canvas, x, y, zoom);
        }
        if (del) {
            delete saved[del];
            localStorage.setItem("quick_coords", JSON.stringify(saved));
            updateList();
        }
        if (copy) {
            navigator.clipboard.writeText(copy).then(() => {
                e.target.innerText = "Copied!";
                setTimeout(() => { e.target.innerText = "Copy"; }, 1000);
            });
        }
    };
    document.getElementById("qc_header").onclick = (e) => {
        if (e.target.id === "qc_theme_toggle") return;
        if (wasDragging) return;
        const content = document.getElementById("qc_content");
        if (content.style.display === "none") {
            expandPanel();
            localStorage.setItem("quick_coords_panel_state", "show");
        } else {
            collapsePanel();
            localStorage.setItem("quick_coords_panel_state", "hide");
        }
    };
    let isDragging = false, offsetX = 0, offsetY = 0;
    panel.addEventListener("mousedown", (e) => {
        if (["qc_save","qc_name","qc_resize","qc_theme_toggle"].includes(e.target.id)) return;
        wasDragging = false;
        isDragging = true;
        offsetX = e.clientX - panel.offsetLeft;
        offsetY = e.clientY - panel.offsetTop;
    });
    document.addEventListener("mousemove", (e) => {
        if (isDragging) {
            wasDragging = true;
            const newLeft = (e.clientX - offsetX) + "px";
            const newTop = (e.clientY - offsetY) + "px";
            panel.style.left = newLeft;
            panel.style.top = newTop;
            localStorage.setItem("quick_coords_panel_pos", JSON.stringify({ left: newLeft, top: newTop }));
        }
    });
    document.addEventListener("mouseup", () => {
        isDragging = false;
    });
    const resizeEl = document.getElementById("qc_resize");
    let isResizing = false, startW, startH, startX, startY;
    resizeEl.addEventListener("mousedown", (e) => {
        if (document.getElementById("qc_content").style.display === "none") return;
        e.stopPropagation();
        isResizing = true;
        startX = e.clientX;
        startY = e.clientY;
        startW = parseInt(window.getComputedStyle(panel).width);
        startH = parseInt(window.getComputedStyle(panel).height);
        panel.style.transition = "none";
    });
    document.addEventListener("mousemove", (e) => {
        if (!isResizing) return;
        const newW = startW + (e.clientX - startX);
        const newH = startH + (e.clientY - startY);
        panel.style.width = Math.max(180, newW) + "px";
        panel.style.height = Math.max(120, newH) + "px";
        if (document.getElementById("qc_content").style.display !== "none") {
            localStorage.setItem("quick_coords_panel_size", JSON.stringify({
                width: panel.style.width,
                height: panel.style.height
            }));
        }
    });
    document.addEventListener("mouseup", () => {
        if (isResizing) {
            isResizing = false;
            panel.style.transition = PANEL_TRANSITION;
        }
    });
})();