pandatype

Scripts for pandatype

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name pandatype
// @namespace http://tampermonkey.net/
// @version 0.72
// @description Scripts for pandatype
// @author kryptonite
// @match https://pandatype.org/*
// @match https://www.pandatype.org/*
// @grant GM_addStyle
// @run-at document-start
// ==/UserScript==

(function() {
        'use strict';

        // 1. Base CSS Style Sheet
        const glassThemeCSS=`:root {
            --glass-bg: rgba(0, 0, 0, .18);
            --glass-border: rgba(255, 255, 255, .08);
            --glass-hover: rgba(255, 159, 47, .35);
            --glass-radius: 24px;
            --blur: 14px;
            --shadow: 0 10px 30px rgba(0, 0, 0, .35);
            --text: #fff;
        }

        html,
        body {
            overscroll-behavior: none;
        }

        body {
            color: var(--text);
            font: 20px "Trebuchet MS", sans-serif;
            text-align: center;
            overflow-x: hidden;
            background: center / cover fixed no-repeat;
            scrollbar-width: none;
            -ms-overflow-style: none;
        }

        body::-webkit-scrollbar {
            display: none;
        }

        a {
            color: inherit;
            text-decoration: none;
        }

        hr {
            margin: 48px;
            border: 0;
            border-top: 2px solid rgba(255, 255, 255, .15);
        }

        /* Glass Components */

        nav,
        .navbar,
        .blurrer {
            background: var(--glass-bg);
            border: 1px solid var(--glass-border);
            border-radius: var(--glass-radius);
            backdrop-filter: blur(var(--blur));
            -webkit-backdrop-filter: blur(var(--blur));
            box-shadow: var(--shadow);
            transition: .25s ease;
        }

        .fancy,
        nav a {
            display: inline-block;
            padding: 10px 14px;
            border-radius: 16px;
            cursor: pointer;
            transition: .2s ease;
        }

        .fancy:hover,
        nav a:hover {
            background: var(--glass-hover);
            text-shadow: 0 0 8px rgba(255, 255, 255, .4);
        }

        /* Background Panel */

        #bg-uploader-panel {
            position: fixed;
            bottom: 20px;
            left: 20px;
            z-index: 999999;
            display: flex;
            align-items: center;
            font: 13px sans-serif;
        }

        #bg-uploader-gear {
            width: 36px;
            height: 36px;
            display: grid;
            place-items: center;
            border-radius: 50%;
            cursor: pointer;
            background: rgba(0, 0, 0, .6);
            backdrop-filter: blur(10px);
            box-shadow: var(--shadow);
            transition: transform .3s;
        }

        #bg-uploader-panel:hover #bg-uploader-gear {
            transform: rotate(90deg);
        }

        #bg-uploader-menu {
            display: none;
            flex-direction: column;
            gap: 10px;
            width: 210px;
            margin-left: 10px;
            padding: 14px;
            border-radius: 16px;
            background: rgba(0, 0, 0, .8);
            border: 1px solid var(--glass-border);
            backdrop-filter: blur(var(--blur));
            box-shadow: var(--shadow);
        }

        #bg-uploader-panel:hover #bg-uploader-menu {
            display: flex;
        }

        body.pt-tape #passageBox {
            overflow: hidden !important;
            height: 100px !important;
        }

        body.pt-tape #passageInner {
            white-space: nowrap !important;
            display: inline-block !important;
            width: max-content !important;
            font-size: 27px !important;
            padding: 20px !important;
            will-change: transform;
        }

        body.pt-tape .ch {
            display: inline-block !important;
        }

        body.pt-tape .ch.space {
            width: .6em !important;
        }

        .bg-btn {
            padding: 7px;
            border: 0;
            border-radius: 8px;
            cursor: pointer;
            color: #fff;
            background: rgba(255, 255, 255, .15);
            transition: background .2s;
        }

        .bg-btn:hover {
            background: var(--glass-hover);
        }

        .discord-btn {
            display: block;
            background: rgba(88, 101, 242, .25);
            border: 1px dashed rgba(88, 101, 242, .45);
        }

        .discord-btn:hover {
            background: rgba(88, 101, 242, .5);
        }

        /* Toggle */

        .switch-container {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-top: 8px;
            padding-top: 8px;
            border-top: 1px solid rgba(255, 255, 255, .1);
        }

        .switch-label-block {
            display: flex;
            flex-direction: column;
        }

        .credits-tag {
            font-size: 9px;
            color: rgba(255, 255, 255, .5);
        }

        .toggle-switch {
            position: relative;
            width: 34px;
            height: 20px;
        }

        .toggle-switch input {
            display: none;
        }

        .slider {
            position: absolute;
            inset: 0;
            cursor: pointer;
            border-radius: 20px;
            background: rgba(255, 255, 255, .2);
            transition: .25s;
        }

        .slider::before {
            content: "";
            position: absolute;
            left: 3px;
            bottom: 3px;
            width: 14px;
            height: 14px;
            border-radius: 50%;
            background: #fff;
            transition: .25s;
        }

        input:checked + .slider {
            background: #ff9f2f;
        }

        input:checked + .slider::before {
            transform: translateX(14px);
        }

        `;

        if (typeof GM_addStyle !=='undefined') {
            GM_addStyle(glassThemeCSS);
        }

        else {
            const style=document.createElement('style');
            style.textContent=glassThemeCSS;
            document.head.appendChild(style);
        }

        // 2. Auto-Reloader Script Engine

        const DELAY_MS=0.369;

        let autoReloadEnabled=localStorage.getItem("pt_auto_reload") !=="false";

        let tapeModeEnabled=localStorage.getItem("pt_tape_mode") !=="false";

        let tapeRAF=0;
        let tapeOffset=0;
        let currentPassage="";
        let handledButton=null;

        let passageBox=null;
        let passageInner=null;
        let lastTransform="";

        function getPassageElements() {
            const box=document.getElementById("passageBox");
            const inner=document.getElementById("passageInner");

            if (box !==passageBox) {
                passageBox=box;
            }

            if (inner !==passageInner) {
                passageInner=inner;
            }
        }

        function setupPassage() {

            getPassageElements();

            if ( !passageInner) {
                return;
            }

            const text=passageInner.textContent;

            if (text===currentPassage) {
                return;
            }

            currentPassage=text;

            passageInner.querySelectorAll(".ch").forEach(ch=> {

                    if (ch.textContent===" " || ch.textContent==="") {

                        ch.classList.add("space");
                        ch.innerHTML=" ";

                    }

                });

        }

        function updateTape() {

            const box=document.getElementById("passageBox");
            const inner=document.getElementById("passageInner");
            const current=document.querySelector(".ch.current");

            if (!box || !inner || !current) return;

            const target=box.clientWidth * 0.48 -
                  current.offsetLeft -
                  current.clientWidth / 2;

            const delta = target - tapeOffset;

            if (Math.abs(delta) < 0.01) return;

            tapeOffset+=delta * 0.15;

            inner.style.transform=`translateX(${tapeOffset}px)`;
        }

        function tapeLoop() {
            if ( !tapeModeEnabled) {
                tapeRAF=0; return;
            }

            setupPassage();
            updateTape(); tapeRAF=requestAnimationFrame(tapeLoop);
        }

        function startTape() {
            getPassageElements(); document.body.classList.add("pt-tape");

            currentPassage=""; tapeOffset=0; lastTransform=""; setupPassage(); const current=document.querySelector(".ch.current"); if (passageBox && passageInner && current) {
                tapeOffset=passageBox.clientWidth * 0.42 - current.offsetLeft - current.clientWidth / 2; lastTransform=`translateX(${tapeOffset}px)`;
            passageInner.style.transform=lastTransform;
        }

        if ( !tapeRAF) {
            tapeLoop();
        }
    }

    function stopTape() {
        document.body.classList.remove("pt-tape"); cancelAnimationFrame(tapeRAF);

        tapeRAF=0; tapeOffset=0; currentPassage=""; lastTransform=""; if (passageInner) {
            passageInner.style.transform="";
        }
    }

    const observer=new MutationObserver(()=> {

            if ( !autoReloadEnabled) {
                return;
            }

            const btn=document.getElementById("raceAgainBtn");

            if (btn===handledButton) {
                return;
            }

            handledButton=btn;

            if ( !btn) {
                return;
            }

            requestAnimationFrame(()=> {

                    if (btn.isConnected && !btn.disabled) {
                        btn.click();
                    }

                });

        });

    // 3. Background State & Control Panel UI Injection
    function initThemeEngine() {
        const savedBg=localStorage.getItem('pt_custom_bg');

        if (savedBg) {
            document.body.style.backgroundImage=`url("${savedBg}")`;
        }

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });

    if (tapeModeEnabled) {
        startTape();
    }

    if (document.getElementById('bg-uploader-panel')) return;

    const panel=document.createElement('div');
    panel.id='bg-uploader-panel';

    const gear=document.createElement('div');
    gear.id='bg-uploader-gear';

    gear.innerHTML=` <svg xmlns="http://www.w3.org/2000/svg"
    width="22"
    height="22"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round" > <circle cx="12" cy="12" r="3" /> <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0
 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9A1.65 1.65 0 0 0 10 3.09V3a2 2 0 1 1 4 0v.09A1.65 1.65 0 0 0 15 4.6h.08a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9v.08A1.65 1.65 0 0 0 20.91 10H21a2 2 0 1 1 0 4h-.09A1.65 1.65 0 0 0 19.4 15z"
 /> < /svg>`;

    const menu=document.createElement('div');
    menu.id='bg-uploader-menu';

    const label=document.createElement('span');
    label.style.fontWeight='bold';
    label.style.color='#ff9f2f';

    // File Uploader Elements (Images)
    const fileInput=document.createElement('input');
    fileInput.type='file';
    fileInput.accept='.png, .jpg, .jpeg';
    fileInput.style.display='none';

    const uploadBtn=document.createElement('button');
    uploadBtn.className='bg-btn';
    uploadBtn.textContent='📁 Upload Background';
    uploadBtn.onclick=()=> fileInput.click();

    // Remove Background button
    const resetBtn=document.createElement('button');
    resetBtn.className='bg-btn';
    resetBtn.style.background='rgba(239, 68, 68, 0.25)';
    resetBtn.textContent='❌ Remove Background';

    resetBtn.onclick=()=> {
        localStorage.removeItem('pt_custom_bg');
        document.body.style.backgroundImage='none';
    }

    ;

    // File loading pipeline logic
    const processFile=(file)=> {
        if ( !file) return;
        const reader=new FileReader();

        reader.onload=(event)=> {
            const base64Data=event.target.result;

            try {
                localStorage.setItem('pt_custom_bg', base64Data);
                document.body.style.backgroundImage=`url("${base64Data}")`;
            }

            catch (error) {
                alert("This file is too large for storage.");
            }
        }

        ;
        reader.readAsDataURL(file);
    }

    ;

    fileInput.onchange=(e)=> processFile(e.target.files[0]);

    // Auto Reloader Toggle Element
    const switchContainer=document.createElement('div');
    switchContainer.className='switch-container';

    const labelBlock=document.createElement('div');
    labelBlock.className='switch-label-block';

    const switchText=document.createElement('span');
    switchText.style.fontWeight='bold';
    switchText.style.color='#ffffff';
    switchText.textContent='Reloader';
    labelBlock.appendChild(switchText);

    const toggleLabel=document.createElement('label');
    toggleLabel.className='toggle-switch';

    const toggleInput=document.createElement('input');
    toggleInput.type='checkbox';
    toggleInput.checked=autoReloadEnabled;

    toggleInput.addEventListener('change', e=> {

            autoReloadEnabled=e.target.checked;

            localStorage.setItem('pt_auto_reload',
                String(autoReloadEnabled));

            console.log(`[PandaTree] Auto Reload $ {
                    autoReloadEnabled ? "Enabled" : "Disabled"
                }

                `);

        });

    const sliderSpan=document.createElement('span');
    sliderSpan.className='slider';

    toggleLabel.appendChild(toggleInput);
    toggleLabel.appendChild(sliderSpan);
    switchContainer.appendChild(labelBlock);
    switchContainer.appendChild(toggleLabel);

    // Assembly
    menu.appendChild(label);
    menu.appendChild(uploadBtn);
    menu.appendChild(resetBtn);
    menu.appendChild(switchContainer);

    // Tape Mode Toggle
    const tapeContainer=document.createElement('div');
    tapeContainer.className='switch-container';

    const tapeLabelBlock=document.createElement('div');
    tapeLabelBlock.className='switch-label-block';

    const tapeText=document.createElement('span');
    tapeText.style.fontWeight='bold';
    tapeText.style.color='#ffffff';
    tapeText.textContent='Tape Mode';

    tapeLabelBlock.appendChild(tapeText);

    const tapeToggleLabel=document.createElement('label');
    tapeToggleLabel.className='toggle-switch';

    const tapeToggleInput=document.createElement('input');
    tapeToggleInput.type='checkbox';
    tapeToggleInput.checked=tapeModeEnabled;

    tapeToggleInput.addEventListener("change", e=> {

            tapeModeEnabled=e.target.checked;

            localStorage.setItem("pt_tape_mode",
                tapeModeEnabled);

            if (tapeModeEnabled) {
                startTape();
            }

            else {
                stopTape();
            }

        });

    const tapeSlider=document.createElement('span');
    tapeSlider.className='slider';

    tapeToggleLabel.appendChild(tapeToggleInput);
    tapeToggleLabel.appendChild(tapeSlider);

    tapeContainer.appendChild(tapeLabelBlock);
    tapeContainer.appendChild(tapeToggleLabel);

    menu.appendChild(tapeContainer);

    panel.appendChild(gear);
    panel.appendChild(menu);

    document.body.appendChild(panel);
}

if (document.readyState==='loading') {
    document.addEventListener('DOMContentLoaded', initThemeEngine);
}

else {
    initThemeEngine();
}
})();