QuickCoords

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

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==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;
        }
    });
})();