Greasy Fork is available in English.
Scripts for pandatype
// ==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();
}
})();