Pixeltracer -- PixelPlace Image Overlay Tool

A script that allows you to place templates over the canvas, it's easy to use and includes (almost) all fundamental features of a template tool.

// ==UserScript==
// @name         Pixeltracer -- PixelPlace Image Overlay Tool
// @namespace    https://pxphub.neocities.org/
// @version      0.20.0
// @description  A script that allows you to place templates over the canvas, it's easy to use and includes (almost) all fundamental features of a template tool.
// @author       Realwdpcker
// @license      MIT
// @match        https://pixelplace.io/*-*
// @icon         data:image/png;base64,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
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @require      https://pixelplace.io/js/jquery.min.js?v2=1
// @require      https://code.jquery.com/ui/1.14.1/jquery-ui.min.js
// @require      https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.js
// @resource     notyfCSS https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.css
// ==/UserScript==

/*
 * The icon used in this script is licensed under CC0 1.0 Universal (Public Domain).
 * See https://creativecommons.org/publicdomain/zero/1.0/ for more details.
 *
 * This script is distributed "as-is", without warranties.
 *
 * This code must not be sold, neither alone or as part of a bundle. If you paid for this script or received it as part of a bundle, please demand your money back immediately.
 * This code may also not be allowed to be processed through and/or modified with generative AI or generative tools AND/OR be modified without attribution to the original developer, if you so wish to, please contact the developer at @guildedbirdalt on Discord.
 */

(function() {
    'use strict';

    GM_addStyle(`
    @import url('https://fonts.googleapis.com/css2?family=Inconsolata&family=Lexend:[email protected]&family=Mulish:ital,wght@0,200..1000;1,200..1000&display=swap');
    [material-icons] {
      font-family: 'Material Icons';
      font-weight: normal;
      font-style: normal;
      font-size: 24px;
      display: inline-block;
      line-height: 1;
      letter-spacing: normal;
      text-transform: none;
      white-space: nowrap;
      direction: ltr;
      -webkit-font-feature-settings: 'liga';
      -webkit-font-smoothing: antialiased;
    }
    .pt-ui {
    width: 200px;
    height: auto;
    background-color: #222222;
    z-index: 1000;
    display: none;
    position: absolute;
    top: 170px;
    left: 65px;
    padding: 5px;
    border-radius: 5px;
    border: #ffffff 2px solid;
    }
    .pt-settings-ui {
    width: auto;
    height: auto;
    background-color: #222222;
    z-index: 1000;
    display: none;
    position: absolute;
    top: 170px;
    left: 270px;
    padding: 5px;
    border-radius: 5px;
    border: #ffffff 2px solid;
    color: white;
    flex-direction: column;
    }
    .pt-ui input[type=range]::-webkit-slider-runnable-track {
  background-color: transparent;
  border-radius: 5px;
  border: 1px solid #444444;
  }
  .opacity-container input[type=range] {
  margin: 0px !important;
  }
  .pt-ui input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  background-color: white;
  border-radius: 5px;
  cursor: pointer;
  height: 10px;
  width: 10px;
  margin-top: 0.5px;
  transition: transform 0.1s ease-in-out;
    }
    .pt-ui input[type=range]::-webkit-slider-thumb:hover {
  transform: scale(1.75);
    }
    .x-header, .y-header, .scale-header, .opacity-header {
    font-size: 14px;
    color: white;
    font-family: Lexend, sans-serif;
    display: flex;
    align-items: center;
    column-gap: 2.5px;
    }
    .x-coordinate, .y-coordinate, .scale-input, .opacity-input {
    font-size: 14px;
    color: white;
    font-family: Mulish, sans-serif;
    }
    .x-coordinate, .y-coordinate, .scale-input {
    width: 70px;
    }
    .x-container, .y-container, .scale-container, .opacity-container {
    display: flex;
    column-gap: 2.5px;
    }
    .pt-label {
    font-size: 16px;
    color: white;
    font-family: Lexend, sans-serif;
    }
    .pt-version {
    font-size: 12px;
    color: #555555 !important;
    text-decoration: none;
    font-family: Lexend, sans-serif;
    }
    .pt-version:hover {
    color: #666666 !important;
    text-decoration: underline;
    }
    .hz-composite-divider {
    height: 2px;
    width: auto;
    z-index: 1001;
    position: relative;
    background: linear-gradient(90deg,rgba(83, 83, 83, 1) 0%, rgba(83, 83, 83, 1) 30%, rgba(83, 83, 83, 1) 70%, rgba(0, 0, 0, 0) 100%);
    margin: 3px 0px 3px 0px;
    }
    .vt-composite-divider {
    height: auto;
    width: 2px;
    z-index: 1001;
    position: relative;
    background: linear-gradient(180deg,rgba(0, 0, 0, 0) 0%, rgba(83, 83, 83, 1) 30%, rgba(83, 83, 83, 1) 70%, rgba(0, 0, 0, 0) 100%);
    margin: 0px 3px 0px 3px;
    }
    .header-container {
    display: flex;
    justify-content: space-between;
    }
    .action-buttons {
    display: flex;
    width: auto;
    gap: 5px;
    }
    .action-buttons > button {
    background-color: transparent !important;
    margin: 0px;
    padding: 0px;
    }
    .action-buttons > button:hover {
    background-color: transparent !important;
    filter: brightness(0.5);
    cursor: pointer;
}
.action-buttons > button:disabled,
.action-buttons > button[disabled] {
    filter: none !important;
    pointer-events: none !important;
    cursor: not-allowed !important;
    opacity: 0.5;
}
    .pt-overlay {
    position: absolute;
	top: 0;
	left: 0;
	pointer-events: none;
	z-index: 1000;
    }
    .tools-container {
    row-gap: 5px;
    display: flex;
    flex-direction: column;
    }
    .settings-btn, .minimize-ui-btn, .close-ui-btn {
    background-color: transparent !important;
    margin: 0px;
    padding: 0px;
    font-size: 14px;
    }
    .pt-settings-bg {
    display: none;
    width: 100vw;
    height: 100vh;
    background-color: #00000080;
    backdrop-filter: blur(2px);
    top: 0;
    position: absolute;
    }
    .file-reader {
    display: none;
    }
    .settings-container {
    display: flex;
    flex-direction: column;
    row-gap: 5px;
    font-size: 14px;
    color: white;
    font-family: Mulish, sans-serif;
    }
    .settings-container label, .hotkey-container label, .extra-container label, .hotkey-header-label {
    font-size: 14px;
    color: white;
    font-family: Mulish, sans-serif;
    }
    .hotkey-container label {
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
    text-decoration: dotted;
    }
    .hotkey-container label:hover {
    cursor: url("data:image/png;base64,AAACAAEAICAAAAAAAACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2Nj/2NjY/9jY2P/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNjY/9jY2P/Y2Nj/2NjY/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABjY2P/Y2Nj/2NjY/9jY2P/Y2Nj/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2Nj/2NjY/9jY2P/Y2Nj/2NjY/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNjY/9jY2P/Y2Nj/2NjY/9jY2P/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ6env+enp7/np6e/56env9jY2P/Y2Nj/2NjY/9jY2P/Y2Nj/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMz//////////////////////56env9jY2P/Y2Nj/2NjY/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzM/7W1tf////8+////Pv///z7///8+/////56env9jY2P/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////+enp7/////Pv///z7///8+////Pv///z7///8+/////56env8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////56env////8+////Pv///z7///8+////Pv///z7/////np6e/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////np6e/////z7///8+////Pv///z7///8+////Pv////+enp7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////+enp7/////Pv///z7///8+////Pv///z7///8+/////56env8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////+enp7/////Pv///z7///8+////PrW1tf/MzMz/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////+enp7/np6e/56env+enp7/zMzM/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////8f///+H////B////g////wf//+AP///AH///nj///z8///8/P///Pz///z8///+ef///wP///+H///8=") 0 0, auto;
    text-decoration: underline;
    }
    .extra-container label:hover {
    text-decoration: underline;
    cursor: pointer;
    }
    .hotkey-label {
    background: #333333;
    border: 1px solid #444444;
    border-radius: 4px;
    color: white;
    display: inline-block;
    font-family: Mulish, sans-serif;
    font-size: 11px;
    line-height: 1.5;
    margin: 0 .1em .1em .1em;
    padding: .1em .7em;
    text-indent: 0;
    cursor: default;
    text-transform: uppercase;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    box-shadow: inset -2px -2px 1px 0px rgba(0, 0, 0, 0.15);
}
.menu-container {
display: flex;
flex-direction: column;
}
.notyf__toast {
  transform-origin: center center;
  will-change: transform, opacity;
  position: relative;
  overflow: hidden;
  transform: none !important;
  cursor: pointer;
  max-width: none !important
}
.notyf__message {
  animation: none !important;
  transition: none !important;
  opacity: 1 !important;
  transform: none !important;
}
.notyf__toast--warning {
  background: orange !important;
}
.notyf.disabled {
  display: none !important;
}
.canvas-blending-switcher {
color: white;
font-family: Mulish, sans-serif;
}
.pt-settings-ui select option {
background-color: #222222;
font-family: Mulish, sans-serif;
}
    `);

    let img = new Image();
    let imgDataURL = null;
    let posX = 0, posY = 0, scale = 1, opacity = 0.5;
    let rotation = 0;

    const overlayElem = document.createElement("canvas");
    overlayElem.className = "pt-overlay"
    const settingsBG = document.createElement("div");
    settingsBG.className = "pt-settings-bg"
    const fileReader = $('<input type="file" accept="image/*" class="file-reader">')[0]
    const materialIcons = document.createElement('link');
    materialIcons.href = 'https://fonts.googleapis.com/icon?family=Material+Icons';
    materialIcons.rel = 'stylesheet';
    const jQCss = document.createElement('link');
    jQCss.rel = 'stylesheet';
    jQCss.href = 'https://code.jquery.com/ui/1.14.1/themes/base/jquery-ui.css';
    document.head.append(materialIcons, jQCss);
    const notyf = new Notyf({
        ripple: false,
        duration: 2000,
        maxVisibleNotifications: 1,
        position: { x: 'center', y: 'top' }
    });
    GM_addStyle(GM_getResourceText("notyfCSS"));
    let menuButtons = $('#menu-buttons')
    const openPTButton = $('<a href="#" title="Open Pixeltracer" id="open-pixeltracer-button" class="grey margin-top-button"><img src="https://i.imgur.com/tLWE5SR.png" alt="icon"></a>')

    const pixeltracerUI = document.createElement('div');
    pixeltracerUI.className = "pt-ui";
    pixeltracerUI.innerHTML = `
    <div class="header-container">
    <div class="header-content-container">
    <label class="pt-label">Pixeltracer</label>
    </div>
    <div class="ui-button-container">
    <button class="settings-btn" title="Minimize..." material-icons>settings</button>
    <button class="minimize-ui-btn" title="Minimize..." material-icons>minimize</button>
    <button class="close-ui-btn" title="Close..." material-icons>close</button>
    </div>
    </div>
    <div class="components-container">
    <div class="hz-composite-divider"></div>
    <div class="action-buttons">
    <button class="image-upload-btn" title="Open an image..." material-icons>upload</button>
    <button disabled class="remove-image-btn" title="Remove overlay..." material-icons>delete</button>
    <button disabled class="hide-overlay-btn" title="Hide overlay..." material-icons>visibility</button>
    <button disabled class="reset-overlay-btn" title="Reset overlay..." material-icons>cleaning_services</button>
    <button disabled class="flip-overlay-btn" title="Flip overlay..." material-icons>flip</button>
    <button disabled class="rotate-overlay-btn" title="Rotate overlay..." material-icons>rotate_90_degrees_cw</button>
    </div>
    <div class="tools-container">
    <div class="x-container">
    <label class="x-header">X: </label>
    <input class="x-coordinate" type="number" step="1" value="${posX}"></input>
    </div>
    <div class="y-container">
    <label class="y-header">Y: </label>
    <input class="y-coordinate" type="number" step="1" value="${posY}"></input>
    </div>
    <div class="scale-container">
    <label class="scale-header">Scale: </label>
    <input class="scale-input" type="number" step="0.01" value="${scale}"></input>
    </div>
    <div class="opacity-container">
    <label class="opacity-header">Opacity: </label>
    <input class="opacity-input" type="range" min="0" max="1" step="0.01" value="${opacity}"></input>
    </div>
    </div>
    </div>
    `

    const pixeltracerSettingsUI = document.createElement('div');
    pixeltracerSettingsUI.className = "pt-settings-ui";
    pixeltracerSettingsUI.innerHTML = `
    <div class="header-container">
    <div class="header-content-container">
    <label class="pt-label">Settings</label>
    </div>
    </div>
    <div class="hz-composite-divider"></div>
    </div>
    <div class="menu-container">
    <div class="settings-container">
    <label><input type="checkbox" class="notification-toggle" checked> Show notifications</label>
    <select class="canvas-blending-switcher">
    <option value="" disabled selected>Select blending mode...</option>
    <option value="normal">Normal</option>
    <option value="multiply">Multiply</option>
    <option value="screen">Screen</option>
    <option value="overlay">Overlay</option>
    <option value="darken">Darken</option>
    <option value="lighten">Lighten</option>
    <option value="color-dodge">Color Dodge</option>
    <option value="color-burn">Color Burn</option>
    <option value="hard-light">Hard Light</option>
    <option value="soft-light">Soft Light</option>
    <option value="difference">Difference</option>
    <option value="exclusion">Exclusion</option>
    <option value="hue">Hue</option>
    <option value="saturation">Saturation</option>
    <option value="color">Color</option>
    <option value="luminosity">Luminosity</option>
    </select>
    </div>
    <div class="header-container">
    <div class="header-content-container">
    <label class="pt-label">Hotkeys</label>
    </div>
    </div>
    <div class="hz-composite-divider"></div>
    </div>
    <div class="hotkey-container">
    <span class="hotkey-header-label">Hover over a hotkey to view the info.</span><br>
    <div class="hotkey-label">SHIFT</div><label title="SHIFT + Right click to jump the overlay to your mouse position.">+ Right Click</label><br>
    <div class="hotkey-label">SHIFT</div><label title="Hold SHIFT + W + Left click to drag the overlay around.">+ W + Left Click</label><br>
    <div class="hotkey-label">SHIFT</div><label title="SHIFT + Q to enable auto color picking.">+ Q</label><br>
    <div class="hotkey-label">CTRL</div><label title="SHIFT + Middle click to select the pixel color at your current mouse position.">+ Middle Click</label>
    </div>
    <div class="extra-container">
    <div class="hz-composite-divider"></div>
    <label class="pt-view-1">View script info</label><br>
    <label class="pt-view-2">PXP Hub Website</label><br>
    <label class="pt-view-3">PXP Hub Discord</label>
    </div>
    </div>
    </div>
    </div>`

    menuButtons.append(openPTButton)
    document.body.append(pixeltracerUI, settingsBG, pixeltracerSettingsUI, fileReader, pixeltracerUI);

    /* start of ui interface dragging */
    let uiOpened = false;
    const ptUI = $(".pt-ui")[0];
    const ptSettingsUI = $(".pt-settings-ui")[0];
    const newOpenPTButton = $("#open-pixeltracer-button")[0];

    $(".pt-ui, .pt-settings-ui").draggable({
        containment: "window",
        snap: ".pt-ui,.pt-settings-ui",
        snapMode: "outer",
        stack: ".pt-ui,.pt-settings-ui",
        snapTolerance: 5
    });
    /* end of ui interface dragging */

    newOpenPTButton.addEventListener('click', (e) => {
        uiOpened = !uiOpened;
        ptUI.style.display = uiOpened ? 'block' : 'none';
    });

    /* start of overlay placement handling */
    const xValue = ptUI.querySelector('.x-coordinate');
    const yValue = ptUI.querySelector('.y-coordinate');
    const scaleValue = ptUI.querySelector('.scale-input');
    const opacityValue = ptUI.querySelector('.opacity-input');
    xValue.addEventListener("input", () => {
        posX = parseFloat(xValue.value) || 0;
        drawOverlay();
    });
    yValue.addEventListener("input", () => {
        posY = parseFloat(yValue.value) || 0;
        drawOverlay();
    });
    scaleValue.addEventListener("input", () => {
        const value = parseFloat(scaleValue.value);
        scale = value <= 0 ? 1 : value;
        drawOverlay();
    });
    opacityValue.addEventListener("input", () => {
        overlayElem.style.opacity = parseFloat(opacityValue.value);
        drawOverlay();
    });
    /* end of overlay placement handling */

    function antiNotificationStack() {
        document.querySelectorAll('.notyf__toast').forEach(el => el.remove());
    }
    function convertHexToRGB(hex) {
        const regex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return regex ? [parseInt(regex[1], 16), parseInt(regex[2], 16), parseInt(regex[3], 16)] : null;
    }

    function getConvertedRGBs() {
        const pixelplacePaletteColors = [];
        document.querySelectorAll("#palette-buttons a").forEach(a => {
            const hexFinder = a.getAttribute("title");
            const startRGBConversion = convertHexToRGB(hexFinder);
            if (startRGBConversion) pixelplacePaletteColors.push(startRGBConversion);
        });
        return pixelplacePaletteColors;
    }

    function scaleToCanvas() {
        const originalCanvas = document.getElementById("canvas");
        if (!originalCanvas) return;
        overlayElem.width = originalCanvas.width;
        overlayElem.height = originalCanvas.height;
        overlayElem.style.width = `${originalCanvas.width}px`;
        overlayElem.style.height = `${originalCanvas.height}px`;
        overlayElem.style.imageRendering = "pixelated";
        const container = document.getElementById("painting-move");
        if (container && !container.contains(overlayElem)) {
            container.appendChild(overlayElem);
        }
    }

    function getNearestPaletteColor(r, g, b, palette) {
        let minDist = Infinity,
            best = [r, g, b];
        for (const [pr, pg, pb] of palette) {
            const dist = (r - pr) ** 2 + (g - pg) ** 2 + (b - pb) ** 2;
            if (dist < minDist) {
                minDist = dist;
                best = [pr, pg, pb];
            }
        }
        return best;
    }
    let flipped = false;
    let rotated = false;
    function drawOverlay() {
        if (!imgDataURL || !img.complete || img.naturalWidth === 0) return;

        const ctx = overlayElem.getContext("2d");
        ctx.setTransform(1, 0, 0, 1, 0, 0);
        ctx.imageSmoothingEnabled = false;
        ctx.clearRect(0, 0, overlayElem.width, overlayElem.height);

        const off = document.createElement("canvas");
        off.width = img.naturalWidth;
        off.height = img.naturalHeight;
        const offCtx = off.getContext("2d");
        offCtx.drawImage(img, 0, 0);

        let imageData = offCtx.getImageData(0, 0, off.width, off.height);
        const palette = getConvertedRGBs();

        const drawX = Math.round(posX);
        const drawY = Math.round(posY);
        const drawW = Math.round(off.width * scale);
        const drawH = Math.round(off.height * scale);
        ctx.save();
        ctx.translate(drawX + drawW / 2, drawY + drawH / 2);
        ctx.rotate(rotation * Math.PI / 180);
        if (flipped) {
            ctx.scale(-1, 1);
        }
        ctx.drawImage(
            off,
            0, 0, off.width, off.height,
            -drawW / 2, -drawH / 2, drawW, drawH
        );
        ctx.restore();
    };
    img.onload = () => {
        scaleToCanvas()
        drawOverlay()
    }
    const closeUIButton = ptUI.querySelector(".close-ui-btn");
    closeUIButton.onclick = () => {
        pixeltracerUI.style.display = "none";
        uiOpened = false;
    }
    let minimized = false; let hidden = false; let opened = false; let notyfChecked = false; let notificationsEnabled = true;
    const components = ptUI.querySelector(".components-container");
    const minimizeUIButton = ptUI.querySelector(".minimize-ui-btn");
    minimizeUIButton.onclick = () => {
        minimized = !minimized;
        components.style.display = minimized ? 'none' : 'block';
    }
    const openImageButton = ptUI.querySelector(".image-upload-btn");
    openImageButton.onclick = () => fileReader.click();
    fileReader.onchange = e => {
        const file = e.target.files[0];
        if (!file) return;
        const reader = new FileReader();
        reader.onload = evt => {
            removeImageButton.disabled = false;
            hideOverlayButton.disabled = false;
            resetOverlayButton.disabled = false;
            flipOverlayButton.disabled = false;
            rotateOverlayButton.disabled = false;
            imgDataURL = evt.target.result;
            img.src = imgDataURL;
            overlayElem.style.opacity = parseFloat(opacityValue.value);
        };
        reader.readAsDataURL(file);
    };

    const removeImageButton = ptUI.querySelector(".remove-image-btn");
    removeImageButton.onclick = () => {
        imgDataURL = "";
        img.src = "";
        removeImageButton.disabled = true;
        hideOverlayButton.disabled = true;
        resetOverlayButton.disabled = true;
        flipOverlayButton.disabled = true;
        rotateOverlayButton.disabled = true;
        overlayElem.getContext("2d").clearRect(0, 0, overlayElem.width, overlayElem.height);
        fileReader.value = "";
        antiNotificationStack();
        notyf.success(`Overlay removed successfully`);
    };
    const rotateOverlayButton = ptUI.querySelector(".rotate-overlay-btn");
    rotateOverlayButton.onclick = () => {
        rotation = (rotation + 90) % 360;
        drawOverlay();
    }
    const hideOverlayButton = ptUI.querySelector(".hide-overlay-btn");
    hideOverlayButton.onclick = () => {
        opened = !opened;
        hideOverlayButton.textContent = opened ? 'visibility_off' : 'visibility';
        overlayElem.style.display = opened ? 'none' : 'block';
        if (opened) {
            antiNotificationStack();
            notyf.open({
                type: 'warning', className: 'notyf__toast--warning', message: `Overlay hidden at coordinate: ${posX} & ${posY}`, icon: {
                    className: 'material-icons',
                    tagName: 'i',
                    text: 'warning'
                }});
        } else {
            antiNotificationStack();
            notyf.success(`Overlay shown at coordinate: ${posX} & ${posY}`);
        }
    };
    const settingsButton = ptUI.querySelector(".settings-btn");
    settingsButton.onclick = () => {
        hidden = !hidden;
        pixeltracerSettingsUI.style.display = hidden ? 'flex' : 'none';
    };
    const resetOverlayButton = ptUI.querySelector(".reset-overlay-btn");
    resetOverlayButton.onclick = () => {
        posX = 0;
        ptUI.querySelector('.x-coordinate').value = `${posX}`;
        posY = 0;
        ptUI.querySelector('.y-coordinate').value = `${posY}`;
        scale = 1;
        ptUI.querySelector('.scale-input').value = `${scale}`;
        overlayElem.style.opacity = "0.5";
        ptUI.querySelector('.opacity-input').value = `${opacity}`;
        rotation = 0;
        flipped = false;
        drawOverlay()
        antiNotificationStack();
        notyf.success(`Overlay reset successfully`);
    };
    const notificationsCheckbox = ptSettingsUI.querySelector(".notification-toggle");

    notificationsCheckbox.onclick = () => {
        notificationsEnabled = !notificationsEnabled;

        if (!notificationsEnabled) {
            document.querySelectorAll('.notyf__toast').forEach(el => el.remove());
            notyf.dismissAll();
        }
    };
    const flipOverlayButton = ptUI.querySelector(".flip-overlay-btn");
    flipOverlayButton.onclick = () => {
        flipped = !flipped;
        drawOverlay();
    }
    const bindNotyf = notyf.open.bind(notyf);
    notyf.open = function (options) {
        if (!notificationsEnabled) return null;
        return bindNotyf(options);
    };

    document.addEventListener('click', (e) => {
        if (!notificationsEnabled) return;

        const toast = e.target.closest('.notyf__toast');
        if (toast) {
            toast.remove();
        }
    });
    const viewScriptInfoLabel = ptSettingsUI.querySelector(".pt-view-1");
    const viewScriptWebsite = ptSettingsUI.querySelector(".pt-view-2");
    const viewScriptDiscord = ptSettingsUI.querySelector(".pt-view-3");
    viewScriptInfoLabel.onclick = () => {
        alert(`You are currently running version 0.20.0 of Pixeltracer`);
    };
    viewScriptWebsite.onclick = () => {
        if (!confirm("Visit the PXP Hub Website? This will redirect you off this page.")) return;
        window.open("https://pxphub.neocities.org/pixeltracer", "_blank");
    };
    viewScriptDiscord.onclick = () => {
        if (!confirm("Join the PXP Hub Community Discord? This will redirect you off this page.")) return;
        window.open("https://discord.gg/KgYavKWXSN", "_blank");
    };
    const overlayBlendingSelect = document.querySelector(".canvas-blending-switcher");
    overlayBlendingSelect.addEventListener("change", e => {
        overlayElem.style.mixBlendMode = e.target.value;
    });
    let colorPick = null;

    let isDragging = false;
    let dragOffsetX = 0;
    let dragOffsetY = 0;
    let wPressed = false;
    let autoColorPick = false;
    // e button 0 is left click, e button 1 is middle click, e button 2 is right click
    // turned the color picking block into a global function so I don't have to copy and paste the block everytime
    function colorPickFunc(clickX, clickY) {
        let relX = Math.floor((clickX - posX) / scale);
        let relY = Math.floor((clickY - posY) / scale);

        const w = img.naturalWidth || img.width;
        const h = img.naturalHeight || img.height;
        const cx = w / 2;
        const cy = h / 2;

        if (rotation) {
            const rad = -rotation * Math.PI / 180;
            let dx = relX - cx;
            let dy = relY - cy;
            const cos = Math.cos(rad);
            const sin = Math.sin(rad);
            relX = Math.floor(dx * cos - dy * sin + cx);
            relY = Math.floor(dx * sin + dy * cos + cy);
        }

        if (flipped) {
            relX = w - 1 - relX;
        }

        const srcW = w;
        const srcH = h;
        if (relX < 0 || relY < 0 || relX >= srcW || relY >= srcH) return;

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

        const data = tempCtx.getImageData(relX, relY, 1, 1).data;
        const [r, g, b, a] = data;
        if (a < 10) return;

        colorPick = { r, g, b };
    }
    function selectFoundColor() {
        if (!colorPick) return;

        const { r, g, b } = colorPick;
        const palette = getConvertedRGBs();
        const [nr, ng, nb] = getNearestPaletteColor(r, g, b, palette);

        document.querySelectorAll("#palette-buttons a").forEach(a => {
            const hex = a.getAttribute("title");
            const rgb = convertHexToRGB(hex);
            if (!rgb) return;
            const [pr, pg, pb] = rgb;
            if (pr === nr && pg === ng && pb === nb) {
                a.click();
            }
        });
    }
    document.addEventListener("keydown", e => {
        if (e.key?.toLowerCase() === "w") { wPressed = true };
        if (e.key?.toLowerCase() === "q" && e.shiftKey && !e.repeat) { autoColorPick = !autoColorPick; if (autoColorPick) { antiNotificationStack(); notyf.success('Auto click picking enabled');} else if (!autoColorPick) { antiNotificationStack(); notyf.open({
            type: 'warning',
            className: 'notyf__toast--warning',
            message: `Auto click picking disabled`,
            icon: {
                className: 'material-icons',
                tagName: 'i',
                text: 'warning'
            }
        });}
                                                                     };
    });
    document.addEventListener("keyup", e => {
        if (e.key?.toLowerCase() === "w") { wPressed = false };
    });
    let holdNotificationActive = false;
    document.addEventListener("keydown", e => {
        if (e.shiftKey && wPressed && overlayElem?.isConnected) {
            if (!holdNotificationActive) {
                antiNotificationStack();
                notyf.open({
                    type: 'warning',
                    className: 'notyf__toast--warning',
                    message: `Hold to drag mode enabled`,
                    icon: {
                        className: 'material-icons',
                        tagName: 'i',
                        text: 'warning'
                    }
                });
                holdNotificationActive = true;
            }
            const painting = document.querySelector("#painting");
            if (painting) painting.style.pointerEvents = "none";
        }
    });
    document.addEventListener("keyup", e => {
        if (holdNotificationActive) {
            const painting = document.querySelector("#painting");
            if (painting) painting.style.pointerEvents = "";
            antiNotificationStack();
            notyf.success('Hold to drag mode disabled');
            holdNotificationActive = false;
        }
    });
    document.addEventListener("mousedown", e => {
        if (!imgDataURL) return;

        const canvas = document.getElementsByClassName("pt-overlay")[0];
        if (!canvas) return;

        const rect = canvas.getBoundingClientRect();
        const scaleX = canvas.width / rect.width;
        const scaleY = canvas.height / rect.height;
        const clickX = (e.clientX - rect.left) * scaleX;
        const clickY = (e.clientY - rect.top) * scaleY;

        if (e.shiftKey && e.button === 2 && e.button !== 1) {
            posX = Math.round(clickX);
            posY = Math.round(clickY);
            drawOverlay();

            xValue.value = posX;
            yValue.value = posY;
        } else if (e.button === 1 && e.ctrlKey) {
            e.preventDefault();
            colorPickFunc(clickX, clickY);
        } else if (e.shiftKey && wPressed && e.button === 0) {
            isDragging = true;
            e.preventDefault();
            dragOffsetX = clickX - posX;
            dragOffsetY = clickY - posY;
        }
    });

    document.addEventListener("mousemove", e => {
        if (!isDragging) return;

        const canvas = document.getElementsByClassName("pt-overlay")[0];
        if (!canvas) return;

        const rect = canvas.getBoundingClientRect();
        const scaleX = canvas.width / rect.width;
        const scaleY = canvas.height / rect.height;
        const moveX = (e.clientX - rect.left) * scaleX;
        const moveY = (e.clientY - rect.top) * scaleY;

        if (isDragging) {
            posX = Math.round(moveX - dragOffsetX);
            posY = Math.round(moveY - dragOffsetY);
            drawOverlay();

            xValue.value = posX;
            yValue.value = posY;
        }
    });
    let lastPickedColor = null;
    document.addEventListener("mousemove", e => {
        const canvas = document.getElementsByClassName("pt-overlay")[0];
        if (!canvas) return;
        const rect = canvas.getBoundingClientRect();
        const scaleX = canvas.width / rect.width;
        const scaleY = canvas.height / rect.height;
        const moveX = (e.clientX - rect.left) * scaleX;
        const moveY = (e.clientY - rect.top) * scaleY;
        if (autoColorPick) {
            colorPickFunc(moveX, moveY);
        }
        if (colorPick) {
            const { r, g, b } = colorPick;
            if (!lastPickedColor || r !== lastPickedColor.r || g !== lastPickedColor.g || b !== lastPickedColor.b) {
                lastPickedColor = { r, g, b };
                selectFoundColor();
            }
        }
    });
    document.addEventListener("mouseup", () => {
        if (isDragging) {
            isDragging = false;
        }
        if (colorPick) {
            selectFoundColor()
            colorPick = null;
        }
    });
})();