More Korone

A userscript dedicated to making the Korone site better and easier to use.

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         More Korone
// @description  A userscript dedicated to making the Korone site better and easier to use.
// @namespace    https://www.pekora.zip/
// @version      1.1
// @license      MIT
// @match        https://www.pekora.zip/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @connect      www.pekora.zip
// @connect      i.redd.it
// @connect      cdn.regnum.ru
// @connect      upload.wikimedia.org
// @connect      static.wikia.nocookie.net
// ==/UserScript==

(function () {
    'use strict';


    if (!GM_getValue('onboardingShown', false)) {
        GM_setValue('onboardingShown', true);
        function showOnboarding() {
            const ov = document.createElement('div');
            ov.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.75);z-index:99999;display:flex;align-items:center;justify-content:center;font-family:inherit;';
            const box = document.createElement('div');
            box.style.cssText = 'background:var(--white-color);border:1px solid var(--text-color-quinary);border-radius:8px;padding:32px 36px;max-width:400px;width:90%;text-align:center;color:var(--text-color-primary);box-shadow:0 8px 32px rgba(0,0,0,0.6);';
            box.innerHTML = `
                <h2 style="margin:0 0 12px;font-size:22px;font-weight:700;color:var(--text-color-primary);">Thanks for installing More Korone</h2>
                <p style="margin:0 0 20px;font-size:15px;color:var(--text-color-tertiary);line-height:1.6;">To access your settings, go to<br><strong style="color:var(--text-color-primary);">Settings &rarr; Account Info &rarr; Customize Your Korone</strong><br>and scroll to the bottom.</p>
                <button id="pek-ob-ok" style="background:var(--primary-color);color:#fff;border:none;border-radius:4px;padding:10px 28px;font-size:16px;font-weight:600;cursor:pointer;">Got it!</button>
            `;
            ov.appendChild(box);
            document.body.appendChild(ov);
            ov.querySelector('#pek-ob-ok').addEventListener('click', () => ov.remove());
            ov.addEventListener('click', e => { if (e.target === ov) ov.remove(); });
        }
        if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', showOnboarding);
        else showOnboarding();
    }

    if (location.pathname === '/catalog') {
        const keyword = new URLSearchParams(location.search).get('keyword');
        if (keyword) {
            let injected = false;
            const injectSearch = () => {
                if (injected) return;
                const inp = document.querySelector('input[placeholder="Search"][type="text"]');
                const searchBtn = document.querySelector('[class*="searchButton"]');
                const selectorBtn = document.querySelector('[class*="selectorClosed"]');
                if (!inp || !searchBtn || !selectorBtn) return;
                injected = true;
                obs.disconnect();

                selectorBtn.click();
                requestAnimationFrame(() => {
                    const allOption = Array.from(document.querySelectorAll('[class*="selectOption"]'))
                        .find(el => el.textContent.trim() === 'All');
                    if (allOption) allOption.click();

                    const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
                    nativeSetter.call(inp, keyword);
                    inp.dispatchEvent(new Event('input',  { bubbles: true }));
                    inp.dispatchEvent(new Event('change', { bubbles: true }));

                    requestAnimationFrame(() => searchBtn.click());
                });
            };
            const obs = new MutationObserver(injectSearch);
            obs.observe(document.body, { childList: true, subtree: true });
            injectSearch();
        }
    }

    const DEFAULTS = {
        alertBarHidden:  true,
        logoStyle:       'korone',
        adsHidden:       false,
        hideFeed:        false,
        hideYearText:    false,
        hideRagdoll:     false,
        mergeTix:        false,
        siteAccuracy:    false,
        noTrading:       false,
        textPlayButton:  false,
        friendSuggest:   true,
        koroneToRoblox:  false,
    };

    let barHidden      = GM_getValue('alertBarHidden',  DEFAULTS.alertBarHidden);
    let logoStyle      = GM_getValue('logoStyle',        DEFAULTS.logoStyle);
    let adsHidden      = GM_getValue('adsHidden',        DEFAULTS.adsHidden);
    let hideFeed       = GM_getValue('hideFeed',         DEFAULTS.hideFeed);
    let hideYearText   = GM_getValue('hideYearText',     DEFAULTS.hideYearText);
    let hideRagdoll    = GM_getValue('hideRagdoll',      DEFAULTS.hideRagdoll);
    let mergeTix       = GM_getValue('mergeTix',         DEFAULTS.mergeTix);
    let siteAccuracy   = GM_getValue('siteAccuracy',     DEFAULTS.siteAccuracy);
    let noTrading      = GM_getValue('noTrading',        DEFAULTS.noTrading);
    let textPlayButton = GM_getValue('textPlayButton',   DEFAULTS.textPlayButton);
    let friendSuggest  = GM_getValue('friendSuggest',    DEFAULTS.friendSuggest);
    let koroneToRoblox = GM_getValue('koroneToRoblox',   DEFAULTS.koroneToRoblox);

    const LOGOS = {
        korone:        { desktop: '/img/korone1.png', mobile: '/img/korone-icon-square1.png', favicon: 'https://www.pekora.zip/favicon.ico', external: false, filter: '', faviconFilter: '' },
        pekora:        { desktop: 'https://i.redd.it/sfjq9qtt374f1.png', mobile: 'https://i.redd.it/sfjq9qtt374f1.png', favicon: 'https://www.pekora.zip/favicon.ico', external: true, filter: '', faviconFilter: '' },
        roblox17red:   { desktop: 'https://cdn.regnum.ru/uploads/pictures/news/2021/09/08/regnum_picture_163113324614928_normal.png', mobile: 'https://cdn.regnum.ru/uploads/pictures/news/2021/09/08/regnum_picture_163113324614928_normal.png', favicon: 'https://upload.wikimedia.org/wikipedia/commons/thumb/d/dc/Roblox_icon_-_2017.svg/500px-Roblox_icon_-_2017.svg.png', external: true, filter: '', faviconFilter: '' },
        roblox17white: { desktop: 'https://cdn.regnum.ru/uploads/pictures/news/2021/09/08/regnum_picture_163113324614928_normal.png', mobile: 'https://cdn.regnum.ru/uploads/pictures/news/2021/09/08/regnum_picture_163113324614928_normal.png', favicon: 'https://upload.wikimedia.org/wikipedia/commons/thumb/d/dc/Roblox_icon_-_2017.svg/500px-Roblox_icon_-_2017.svg.png', external: true, filter: 'brightness(0) invert(1)', faviconFilter: 'brightness(0) invert(1)' },
        robloxPre:     { desktop: 'https://upload.wikimedia.org/wikipedia/commons/0/09/Roblox_logo_2015.png', mobile: 'https://upload.wikimedia.org/wikipedia/commons/0/09/Roblox_logo_2015.png', favicon: 'https://static.wikia.nocookie.net/logopedia/images/1/19/ROBLOX_Logo_2015.svg/revision/latest/scale-to-width-down/200?cb=20250518023531', external: true, filter: '', faviconFilter: '' },
    };


    const PAL_HAIR   = 164;
    const MOTORCYCLE = 176;
    const DARK_GREEN = 178;
//add default avatar skin colours here at some point dumbass fuck you
    const MAN_BUNDLE = new Set([167, 168, 169, 171, 172]);

    function isAbandonedLoadout(assetIds) {
        const ids = new Set(assetIds);
        const hasPalHair    = ids.has(PAL_HAIR);
        const hasMotorcycle = ids.has(MOTORCYCLE);
        const hasDarkGreen  = ids.has(DARK_GREEN);
        const hasManBundle  = [...MAN_BUNDLE].some(id => ids.has(id));
        if (hasPalHair && hasMotorcycle && hasManBundle) return 'skip';
        if (hasPalHair && hasDarkGreen  && hasManBundle) return 'skip';
        if (hasPalHair && hasMotorcycle) return 'likely';
        if (hasPalHair && hasDarkGreen)  return 'likely';
        return 'ok';
    }

    const b64cache = {};
    const styleEls = {};
    ['bar','logo','ad','game','feed','year','tix','dropdown','accuracy','trading','play','seeall'].forEach(k => {
        styleEls[k] = document.createElement('style');
        document.head.appendChild(styleEls[k]);
    });

    styleEls.dropdown.textContent = `
        #pek-robux-dropdown{color:var(--text-color-primary);right:5px;left:auto;background-color:var(--white-color);box-shadow:0 -5px 20px rgba(25,25,25,0.15)!important;max-height:266px;border-radius:4px;background-clip:padding-box;font-size:16px;margin:0;padding:0;position:absolute;top:100%;min-width:190px;overflow-x:hidden;overflow-y:auto;list-style:none;text-align:left;z-index:1123;}
        #pek-robux-dropdown li{color:var(--text-color-primary);padding:0;margin:0;white-space:nowrap;width:100%;list-style:none;display:list-item;}
        #pek-robux-dropdown li:hover{background-color:var(--background-color)!important;box-shadow:inset 4px 0 0 0 var(--primary-color);}
        #pek-robux-dropdown li a{padding:10px 12px;display:block;clear:both;line-height:1.428571429;white-space:nowrap;border:none;background:transparent;width:100%;text-align:left;text-decoration:none;cursor:pointer;font-size:16px;user-select:none;color:var(--text-color-primary);}
        #pek-robux-dropdown li a:hover{color:var(--text-color-primary);text-decoration:none;}
        #pek-robux-dropdown li.pek-disabled a{opacity:0.5;cursor:default;pointer-events:none;}
    `;

    function applyBarState()      { styleEls.bar.textContent  = barHidden     ? `[class^="alertBg"],[class*=" alertBg"]{display:none!important}` : ''; }
    function applyYearTextState() { styleEls.year.textContent = hideYearText  ? `[class^="yearText"],[class*=" yearText"]{display:none!important}` : ''; }
    function applyRagdollState()  { styleEls.game.textContent = hideRagdoll   ? `li:has(a[href="/games/127175/Ragdoll-Engine-2020"]){display:none!important}` : ''; }
    function applyTixCSS()        { styleEls.tix.textContent  = mergeTix      ? `[class^="tixContainer"],[class*=" tixContainer"]{display:none!important}` : ''; }

    function applyFeedState() {
        styleEls.feed.textContent = hideFeed ? `
            [class^="myFeedContainer"],[class*=" myFeedContainer"]{display:none!important}
            [class^="blogNewsContainer"],[class*=" blogNewsContainer"]{width:100%!important;max-width:100%!important;flex:0 0 100%!important}
        ` : '';
    }
    function applyAdState() {
        styleEls.ad.textContent = adsHidden ? `
            [class^="adWrapper"],[class*=" adWrapper"],
            [class^="advertisementContainer"],[class*=" advertisementContainer"],
            [class^="skyScraperLeft"],[class*=" skyScraperLeft"],
            [class^="skyScraperRight"],[class*=" skyScraperRight"]{display:none!important}
        ` : '';
        styleEls.seeall.textContent = adsHidden ? `
            [class^="containerHeader"],[class*=" containerHeader"]{display:flex!important;align-items:center!important;justify-content:space-between!important;}
            [class^="containerHeader"] span,[class*=" containerHeader"] span{float:none!important;margin-left:auto!important;}
        ` : '';
        applyLayoutExpansion();
    }
    function applySiteAccuracy() {
        styleEls.accuracy.textContent = siteAccuracy ? `
            a[href="/download"],a[href*="/download"]{display:none!important}
            a[href*="discord.gg"]{display:none!important}
            a[href*="tixexchange"]{display:none!important}
            a[href*="robuxexchange"]{display:none!important}
        ` : '';
    }
    function applyNoTrading() {
        styleEls.trading.textContent = noTrading ? `
            .hover-icon-nav-trade,.hover-icon-nav-message{display:none!important}
            a:has(.hover-icon-nav-trade),a:has(.hover-icon-nav-message){display:none!important}
        ` : '';
    }
    function applyTextPlayButton() {
        styleEls.play.textContent = textPlayButton ? `
            [class^="iconPlay"],[class*=" iconPlay"]{display:none!important}
            button[class*="newBuyButton"]:has([class*="iconPlay"])::before{content:"Play";font-size:20px;font-weight:500;color:#fff;line-height:36px;display:inline-block;vertical-align:middle;}
        ` : '';
    }

    function replaceKoroneText(str) {
        return str
            .replace(/KORONE/g, 'ROBLOX').replace(/Korone/g, 'ROBLOX').replace(/korone/g, 'roblox')
            .replace(/PEKORA/g, 'ROBLOX').replace(/Pekora/g, 'ROBLOX').replace(/pekora/g, 'roblox');
    }

    function applyKoroneToRoblox() {
        if (!koroneToRoblox) return;
        if (/(KORONE|Korone|korone|PEKORA|Pekora|pekora)/.test(document.title))
            document.title = replaceKoroneText(document.title);
        const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
            acceptNode(node) {
                const p = node.parentElement;
                if (!p) return NodeFilter.FILTER_REJECT;
                if (['SCRIPT','STYLE','INPUT','TEXTAREA'].includes(p.tagName)) return NodeFilter.FILTER_REJECT;
                if (p.closest('[data-pek-key="logoStyle"]')) return NodeFilter.FILTER_REJECT;
                return /(KORONE|Korone|korone|PEKORA|Pekora|pekora)/.test(node.textContent) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
            }
        });
        const nodes = []; let n;
        while ((n = walker.nextNode())) nodes.push(n);
        nodes.forEach(node => { node.textContent = replaceKoroneText(node.textContent); });
    }

    let isOnTixItemPage = false;

    function checkTixItemPage() {
        if (!/^\/catalog\/\d+/.test(location.pathname)) {
            if (isOnTixItemPage) revertTixItemBalance();
            isOnTixItemPage = false;
            return;
        }
        const tixPriceIcon = document.querySelector('[class*="priceIcon"] .icon-tix, span.icon-tix[class*="priceIcon"], [class*="priceContainer"] .icon-tix');
        if (!tixPriceIcon) {
            if (isOnTixItemPage) revertTixItemBalance();
            isOnTixItemPage = false;
            return;
        }
        if (isOnTixItemPage) return;
        isOnTixItemPage = true;
        applyTixItemBalance();
    }

    function applyTixItemBalance() {
        if (document.getElementById('pek-tix-balance-inject')) return;
        const tixSpan   = document.querySelector('[class*="tixContainer"] [class*="currencySpan"]');
        const robuxSpan = document.querySelector('[class*="robuxContainer"] [class*="currencySpan"]');
        const robuxIcon = document.querySelector('[class*="robuxContainer"] [class*="currencyIcon"]');
        if (!tixSpan || !robuxSpan) return;
        const badge = document.createElement('span');
        badge.id = 'pek-tix-balance-inject';
        badge.style.cssText = 'color:#fff;font-size:16px;margin-left:5px;margin-right:14px;display:inline;';
        badge.textContent = tixSpan.textContent.trim();
        if (robuxIcon) {
            robuxIcon.dataset.pekOrigClass = robuxIcon.className;
            robuxIcon.className = robuxIcon.className.replace('icon-nav-robux', 'icon-nav-tix');
        }
        robuxSpan.style.display = 'none';
        robuxSpan.insertAdjacentElement('afterend', badge);
    }

    function revertTixItemBalance() {
        document.getElementById('pek-tix-balance-inject')?.remove();
        const robuxSpan = document.querySelector('[class*="robuxContainer"] [class*="currencySpan"]');
        if (robuxSpan) robuxSpan.style.display = '';
        const robuxIcon = document.querySelector('[class*="robuxContainer"] [class*="currencyIcon"]');
        if (robuxIcon?.dataset.pekOrigClass) { robuxIcon.className = robuxIcon.dataset.pekOrigClass; delete robuxIcon.dataset.pekOrigClass; }
    }

    function syncFeedTheme() {
        const iframe = document.getElementById('homepage-iframe-feed');
        if (!iframe) return;
        const inject = () => {
            try {
                const idoc = iframe.contentDocument;
                if (!idoc || !idoc.head) return;
                const bg   = getComputedStyle(document.documentElement).getPropertyValue('--white-color').trim() || '#191919';
                const text = getComputedStyle(document.documentElement).getPropertyValue('--text-color-primary').trim() || '#fff';
                let s = idoc.getElementById('pek-theme-sync');
                if (!s) { s = idoc.createElement('style'); s.id = 'pek-theme-sync'; idoc.head.appendChild(s); }
                s.textContent = `body,html{background:${bg}!important;color:${text}!important;}a{color:${text}!important;}`;
            } catch (e) {}
        };
        iframe.addEventListener('load', inject); inject();
    }

    function applyLayoutExpansion() {
        document.querySelectorAll('.col-lg-9[class*="uselessFuckingClass"],.col-lg-12[class*="uselessFuckingClass"]').forEach(col => {
            if (adsHidden) col.classList.replace('col-lg-9', 'col-lg-12');
            else col.classList.replace('col-lg-12', 'col-lg-9');
        });
        const hc = document.querySelector('[class^="homeContainer"],[class*=" homeContainer"]');
        if (hc) {
            hc.style.flex = adsHidden ? '1 1 100%' : '';
            hc.style.maxWidth = adsHidden ? '100%' : '';
            const fl = hc.querySelector('[class^="friendsList"],[class*=" friendsList"]');
            if (fl) fl.style.flexWrap = adsHidden ? 'wrap' : '';
        }
    }

    function applyFaviconFromCache(url, filter) {
        document.querySelectorAll('link[rel~="icon"]').forEach(el => el.remove());
        if (filter) {
            const img = new Image(); img.crossOrigin = 'anonymous';
            img.onload = () => {
                const canvas = document.createElement('canvas');
                canvas.width = img.width || 64; canvas.height = img.height || 64;
                const ctx = canvas.getContext('2d'); ctx.filter = filter; ctx.drawImage(img, 0, 0);
                const link = document.createElement('link'); link.rel = 'icon'; link.href = canvas.toDataURL();
                link.setAttribute('data-pek-favicon', '1'); document.head.appendChild(link);
            };
            img.onerror = () => { const link = document.createElement('link'); link.rel = 'icon'; link.href = b64cache[url] || url; link.setAttribute('data-pek-favicon', '1'); document.head.appendChild(link); };
            img.src = b64cache[url] || url;
        } else {
            const link = document.createElement('link'); link.rel = 'icon'; link.href = b64cache[url]; link.setAttribute('data-pek-favicon', '1'); document.head.appendChild(link);
        }
    }

    new MutationObserver(() => {
        const def = LOGOS[logoStyle] || LOGOS.korone;
        if (b64cache[def.favicon] && !document.querySelector('link[data-pek-favicon]')) applyFaviconFromCache(def.favicon, def.faviconFilter);
    }).observe(document.head, { childList: true });

    function fetchB64(url, cb) {
        if (b64cache[url]) { cb(b64cache[url]); return; }
        if (url.startsWith('/') || url.startsWith(location.origin)) { b64cache[url] = url; cb(url); return; }
        GM_xmlhttpRequest({ method: 'GET', url, responseType: 'blob',
            onload: res => { const r = new FileReader(); r.onloadend = () => { b64cache[url] = r.result; cb(r.result); }; r.readAsDataURL(res.response); },
            onerror: () => { b64cache[url] = url; cb(url); },
        });
    }

    function applyLogoCSS(desktop, mobile, filter) {
        const f = filter ? `filter:${filter}!important;` : '';
        styleEls.logo.textContent = `
            [class^="imgDesktop"],[class*=" imgDesktop"]{background-image:url("${desktop}")!important;background-size:contain!important;${f}}
            [class^="imgMobile"],[class*=" imgMobile"]{background-image:url("${mobile}")!important;background-size:contain!important;${f}}
        `;
    }
    function applyLogo(key) {
        const def = LOGOS[key] || LOGOS.korone;
        if (!def.external) { applyLogoCSS(def.desktop, def.mobile, ''); b64cache[def.favicon] = def.favicon; applyFaviconFromCache(def.favicon, ''); return; }
        fetchB64(def.desktop, d => fetchB64(def.mobile, m => applyLogoCSS(d, m, def.filter)));
        fetchB64(def.favicon, () => applyFaviconFromCache(def.favicon, def.faviconFilter));
    }

    let tixDropdown = null;
    function removeTixDropdown() { if (tixDropdown) { tixDropdown.remove(); tixDropdown = null; } }

    function claimRobux(tixAmount, onDone) {
        GM_xmlhttpRequest({ method: 'GET', url: 'https://www.pekora.zip/internal/tixexchange',
            onload: res => {
                const doc = new DOMParser().parseFromString(res.responseText, 'text/html');
                const token = doc.querySelector('input[name="__RequestVerificationToken"]');
                if (!token) { onDone(false); return; }
                GM_xmlhttpRequest({ method: 'POST', url: 'https://www.pekora.zip/internal/tixexchange',
                    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                    data: `tix=${encodeURIComponent(tixAmount)}&__RequestVerificationToken=${encodeURIComponent(token.value)}`,
                    onload: r => onDone(r.status < 400), onerror: () => onDone(false),
                });
            },
            onerror: () => onDone(false),
        });
    }

    function openTixDropdown(robuxLi) {
        removeTixDropdown();
        const tixSpan = document.querySelector('[class*="tixContainer"] [class*="currencySpan"]');
        const tix = tixSpan ? (parseInt(tixSpan.textContent.replace(/,/g, ''), 10) || 0) : 0;
        const claimable = Math.floor(tix / 10);
        const ul = document.createElement('ul'); ul.id = 'pek-robux-dropdown';
        function makeItem(label, onClick, disabled) {
            const li = document.createElement('li'); if (disabled) li.className = 'pek-disabled';
            const a = document.createElement('a'); a.href = '#'; a.textContent = label;
            a.addEventListener('click', e => { e.preventDefault(); if (!disabled) onClick(a); });
            li.appendChild(a); ul.appendChild(li);
        }
        makeItem('View Money', () => { removeTixDropdown(); location.href = '/My/Money.aspx'; });
        if (isOnTixItemPage) {
            makeItem('Claim ROBUX (leave tix item first)', null, true);
        } else if (claimable > 0) {
            makeItem(`Claim ROBUX (+${claimable})`, a => {
                a.textContent = 'Converting…'; a.style.opacity = '0.5'; a.style.pointerEvents = 'none';
                claimRobux(tix, ok => { removeTixDropdown(); if (ok) location.reload(); else alert('Conversion failed. Try the Tix Exchange page directly.'); });
            });
        } else {
            makeItem(`Need ${10 - tix} more tix to claim`, null, true);
        }
        tixDropdown = ul; robuxLi.style.position = 'relative'; robuxLi.appendChild(ul);
        setTimeout(() => {
            document.addEventListener('click', function handler(e) {
                if (!robuxLi.contains(e.target)) { removeTixDropdown(); document.removeEventListener('click', handler, true); }
            }, true);
        }, 0);
    }

    function attachTixListener() {
        if (!mergeTix) return;
        const robuxLi = document.querySelector('[class*="robuxContainer"]');
        if (!robuxLi || robuxLi.dataset.pekTix) return;
        robuxLi.dataset.pekTix = '1';
        const link = robuxLi.querySelector('a'); if (!link) return;
        link.addEventListener('click', e => {
            e.preventDefault(); e.stopPropagation();
            if (tixDropdown) removeTixDropdown(); else openTixDropdown(robuxLi);
        });
    }

    function randomUserId() { const a = Math.floor(Math.random() * 100799) + 2, b = Math.floor(Math.random() * 100799) + 2; return Math.max(a, b); }

    function sendFriendRequest(userId, btn) {
        btn.textContent = '...'; btn.disabled = true;
        GM_xmlhttpRequest({ method: 'POST', url: `https://www.pekora.zip/apisite/friends/v1/users/${userId}/request-friendship`,
            headers: { 'Content-Type': 'application/json', 'Content-Length': '0' }, data: '',
            onload: res => { if (res.status < 400) { btn.textContent = 'Pending'; btn.disabled = true; btn.style.opacity = '0.6'; } else { btn.textContent = 'Failed'; btn.disabled = false; } },
            onerror: () => { btn.textContent = 'Error'; btn.disabled = false; },
        });
    }

    function checkAvatarAndAdd(userId, userName, cards, onDone) {
        GM_xmlhttpRequest({ method: 'GET', url: `https://www.pekora.zip/apisite/avatar/v1/users/${userId}/avatar`,
            onload: res => {
                try {
                    const data = JSON.parse(res.responseText);
                    const assetIds = (data.assets || []).map(a => a.id);
                    const verdict = isAbandonedLoadout(assetIds);
                    if (verdict === 'skip') { onDone(false); return; }
                    if (verdict === 'likely' && Math.random() < 0.7) { onDone(false); return; }
                } catch (e) {}
                GM_xmlhttpRequest({ method: 'GET', url: `https://www.pekora.zip/apisite/thumbnails/v1/users/avatar-headshot?userIds=${userId}&size=150x150&format=png`,
                    onload: tres => {
                        let thumbUrl = '/img/placeholder.png';
                        try { thumbUrl = JSON.parse(tres.responseText)?.data?.[0]?.imageUrl || thumbUrl; } catch (e) {}
                        const card = document.createElement('div');
                        card.style.cssText = 'display:flex;flex-direction:column;align-items:center;gap:8px;width:100px;';
                        const link = document.createElement('a'); link.href = `/users/${userId}/profile`;
                        link.style.cssText = 'display:flex;flex-direction:column;align-items:center;gap:6px;text-decoration:none;color:var(--text-color-primary);';
                        const img = document.createElement('img'); img.src = thumbUrl; img.alt = userName; img.onerror = () => { img.src = '/img/placeholder.png'; };
                        img.style.cssText = 'width:64px;height:64px;border-radius:50%;object-fit:cover;background:var(--background-color);border:2px solid var(--text-color-quinary);';
                        const name = document.createElement('span'); name.textContent = userName;
                        name.style.cssText = 'font-size:13px;text-align:center;word-break:break-word;color:var(--text-color-primary);font-weight:500;';
                        link.appendChild(img); link.appendChild(name); card.appendChild(link);
                        const btn = document.createElement('button'); btn.textContent = 'Add Friend';
                        btn.style.cssText = 'color:var(--text-color-primary)!important;border:1px solid var(--text-color-secondary)!important;display:inline-block!important;padding:6px 10px!important;font-size:14px!important;background:var(--white-color)!important;text-align:center!important;font-weight:500!important;line-height:100%!important;user-select:none!important;white-space:nowrap!important;border-radius:3px!important;cursor:pointer!important;font-family:inherit!important;width:100%!important;';
                        btn.addEventListener('mouseover', () => btn.style.boxShadow = '0 1px 3px rgb(150 150 150 / 74%)');
                        btn.addEventListener('mouseout',  () => btn.style.boxShadow = '');
                        btn.addEventListener('click', () => sendFriendRequest(userId, btn));
                        card.appendChild(btn); cards.appendChild(card); onDone(true);
                    },
                    onerror: () => onDone(false),
                });
            },
            onerror: () => onDone(false),
        });
    }

    function applyFriendSuggestions() {
        if (!location.pathname.includes('friends') && !location.href.includes('friend')) { document.getElementById('pek-friend-suggest')?.remove(); return; }
        if (!friendSuggest) { document.getElementById('pek-friend-suggest')?.remove(); return; }
        const h2 = Array.from(document.querySelectorAll('h2')).find(el => el.textContent.includes('FRIEND REQUEST') && /\(0\)/.test(el.textContent));
        if (!h2 || document.getElementById('pek-friend-suggest')) return;
        const box = document.createElement('div'); box.id = 'pek-friend-suggest';
        box.style.cssText = 'margin-top:24px;padding:20px;background:var(--white-color);border-radius:4px;text-align:center;';
        const msg = document.createElement('p'); msg.style.cssText = 'font-size:16px;margin-bottom:20px;color:var(--text-color-primary);';
        msg.textContent = "There are no friend requests for you right now, but that doesn’t stop you from making new friends!";
        box.appendChild(msg);
        const cards = document.createElement('div'); cards.style.cssText = 'display:flex;gap:16px;justify-content:center;flex-wrap:wrap;';
        box.appendChild(cards);
        (h2.closest('.row') || h2.parentElement).insertAdjacentElement('afterend', box);
        const tried = new Set(); let found = 0;
        function tryNextUser() {
            if (found >= 3 || tried.size >= 20) return;
            let id; do { id = randomUserId(); } while (tried.has(id)); tried.add(id);
            GM_xmlhttpRequest({ method: 'GET', url: `https://www.pekora.zip/apisite/users/v1/users/${id}`,
                onload: res => {
                    if (res.status !== 200) { tryNextUser(); return; }
                    try {
                        const user = JSON.parse(res.responseText);
                        if (!user || !user.name || user.name === 'Unknown') { tryNextUser(); return; }
                        checkAvatarAndAdd(id, user.name, cards, added => { if (added) found++; if (found < 3) tryNextUser(); });
                    } catch (e) { tryNextUser(); }
                },
                onerror: () => tryNextUser(),
            });
        }
        tryNextUser(); tryNextUser(); tryNextUser();
    }

    function getCurrentValues() {
        return { alertBarHidden: barHidden, logoStyle, adsHidden, hideFeed, hideYearText, hideRagdoll, mergeTix, siteAccuracy, noTrading, textPlayButton, friendSuggest, koroneToRoblox };
    }

    function applyAll() {
        applyBarState(); applyAdState(); applyRagdollState(); applyFeedState();
        applyYearTextState(); applyTixCSS(); applySiteAccuracy(); applyNoTrading();
        applyTextPlayButton(); applyLogo(logoStyle); syncFeedTheme();
        if (!mergeTix) { revertTixItemBalance(); isOnTixItemPage = false; }
    }
    applyAll();

    function makeRow(labelText, stateKey, options, currentValue, onChange) {
        const ei = document.querySelector('.flex.mt-1 input[readonly]');
        const es = document.querySelector('.flex.mt-1 select');
        const row = document.createElement('div'); row.className = 'flex mt-1';
        const lc = document.createElement('div'); lc.className = 'col pe-0';
        const li = ei ? ei.cloneNode(false) : document.createElement('input');
        li.readOnly = true; li.type = 'text'; li.value = labelText; lc.appendChild(li);
        const sc = document.createElement('div'); sc.className = 'col ps-0 pe-0';
        const sel = es ? es.cloneNode(false) : document.createElement('select');
        sel.dataset.pekKey = stateKey; sel.id = `pek-${stateKey}-select`; sel.innerHTML = '';
        options.forEach(([v, l]) => { const o = document.createElement('option'); o.value = v; o.textContent = l; sel.appendChild(o); });
        sel.value = currentValue;
        sel.addEventListener('change', () => onChange(sel.value));
        sc.appendChild(sel); row.appendChild(lc); row.appendChild(sc);
        return row;
    }

    function makeActionButtons() {
        const w = document.createElement('div'); w.id = 'pek-action-buttons'; w.style.cssText = 'display:flex;gap:8px;margin-top:16px;justify-content:flex-end;';
        const mkBtn = (label, fn) => { const b = document.createElement('button'); b.textContent = label; b.style.cssText = 'border:1px solid var(--text-color-quinary);cursor:pointer;padding:4px 8px;font-size:16px;background:var(--background-color);border-radius:4px;color:var(--text-color-primary);font-family:inherit;'; b.addEventListener('click', fn); return b; };
        w.appendChild(mkBtn('Export Settings', () => { const blob = new Blob([JSON.stringify(getCurrentValues(), null, 2)], { type: 'application/octet-stream' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'pekora-settings.ksf'; a.click(); URL.revokeObjectURL(url); }));
        w.appendChild(mkBtn('Import Settings', () => { const inp = document.createElement('input'); inp.type = 'file'; inp.accept = '.ksf'; inp.addEventListener('change', () => { const f = inp.files[0]; if (!f) return; const r = new FileReader(); r.onload = e => { try { const o = JSON.parse(e.target.result); if(o.alertBarHidden!==undefined){barHidden=!!o.alertBarHidden;GM_setValue('alertBarHidden',barHidden);}if(o.logoStyle!==undefined){logoStyle=o.logoStyle;GM_setValue('logoStyle',logoStyle);}if(o.adsHidden!==undefined){adsHidden=!!o.adsHidden;GM_setValue('adsHidden',adsHidden);}if(o.hideFeed!==undefined){hideFeed=!!o.hideFeed;GM_setValue('hideFeed',hideFeed);}if(o.hideYearText!==undefined){hideYearText=!!o.hideYearText;GM_setValue('hideYearText',hideYearText);}if(o.hideRagdoll!==undefined){hideRagdoll=!!o.hideRagdoll;GM_setValue('hideRagdoll',hideRagdoll);}if(o.mergeTix!==undefined){mergeTix=!!o.mergeTix;GM_setValue('mergeTix',mergeTix);}if(o.siteAccuracy!==undefined){siteAccuracy=!!o.siteAccuracy;GM_setValue('siteAccuracy',siteAccuracy);}if(o.noTrading!==undefined){noTrading=!!o.noTrading;GM_setValue('noTrading',noTrading);}if(o.textPlayButton!==undefined){textPlayButton=!!o.textPlayButton;GM_setValue('textPlayButton',textPlayButton);}if(o.friendSuggest!==undefined){friendSuggest=!!o.friendSuggest;GM_setValue('friendSuggest',friendSuggest);}if(o.koroneToRoblox!==undefined){koroneToRoblox=!!o.koroneToRoblox;GM_setValue('koroneToRoblox',koroneToRoblox);} applyAll(); syncDropdowns(); alert('Settings imported!'); } catch(err){alert('Invalid .ksf file.');} }; r.readAsText(f); }); inp.click(); }));
        w.appendChild(mkBtn('Reset to Default', () => { if (!confirm('Reset all settings to defaults?')) return; Object.entries(DEFAULTS).forEach(([k,v])=>GM_setValue(k,v)); barHidden=DEFAULTS.alertBarHidden;logoStyle=DEFAULTS.logoStyle;adsHidden=DEFAULTS.adsHidden;hideFeed=DEFAULTS.hideFeed;hideYearText=DEFAULTS.hideYearText;hideRagdoll=DEFAULTS.hideRagdoll;mergeTix=DEFAULTS.mergeTix;siteAccuracy=DEFAULTS.siteAccuracy;noTrading=DEFAULTS.noTrading;textPlayButton=DEFAULTS.textPlayButton;friendSuggest=DEFAULTS.friendSuggest;koroneToRoblox=DEFAULTS.koroneToRoblox; revertTixItemBalance(); isOnTixItemPage=false; applyAll(); syncDropdowns(); }));
        return w;
    }

    function syncDropdowns() {
        const map = { alertBarHidden: barHidden?'hidden':'shown', logoStyle, adsHidden: adsHidden?'hidden':'shown', hideFeed: hideFeed?'hidden':'shown', hideYearText: hideYearText?'hidden':'shown', hideRagdoll: hideRagdoll?'hidden':'shown', mergeTix: mergeTix?'enabled':'disabled', siteAccuracy: siteAccuracy?'enabled':'disabled', noTrading: noTrading?'enabled':'disabled', textPlayButton: textPlayButton?'enabled':'disabled', friendSuggest: friendSuggest?'enabled':'disabled', koroneToRoblox: koroneToRoblox?'enabled':'disabled' };
        document.querySelectorAll('[data-pek-key]').forEach(sel => { const v = map[sel.dataset.pekKey]; if (v !== undefined) sel.value = v; });
    }

    function inject() {
        if (document.querySelector('[data-pek-injected]')) return;
        for (const h of document.querySelectorAll('h3')) {
            if (h.textContent.trim() !== 'Customize Your Korone') continue;
            const card = h.nextElementSibling; if (!card) return;
            const marker = document.createElement('div'); marker.setAttribute('data-pek-injected', '1'); card.appendChild(marker);
            const B = (label, key, opts, val, fn) => card.appendChild(makeRow(label, key, opts, val, fn));
            const tog = v => v ? 'enabled' : 'disabled', vis = v => v ? 'hidden' : 'shown';
            B('Alert Bar',           'alertBarHidden',  [['hidden','Hidden'],['shown','Shown']],                                                                                                                  vis(barHidden),       v=>{barHidden=v==='hidden';       GM_setValue('alertBarHidden',barHidden);      applyBarState();});
            B('Logo Style',          'logoStyle',       [['korone','Korone'],['pekora','Pekora'],['roblox17red','ROBLOX (2017 Red)'],['roblox17white','ROBLOX (2017 White)'],['robloxPre','ROBLOX (pre-2017)']],  logoStyle,            v=>{logoStyle=v;                GM_setValue('logoStyle',logoStyle);            applyLogo(v);});
            B('User Ads',            'adsHidden',       [['shown','Shown'],['hidden','Hidden']],                                                                                                                  vis(adsHidden),       v=>{adsHidden=v==='hidden';        GM_setValue('adsHidden',adsHidden);            applyAdState();});
            B('My Feed',             'hideFeed',        [['shown','Shown'],['hidden','Hidden']],                                                                                                                  vis(hideFeed),        v=>{hideFeed=v==='hidden';         GM_setValue('hideFeed',hideFeed);              applyFeedState();});
            B('Year on Game Cards',  'hideYearText',    [['shown','Shown'],['hidden','Hidden']],                                                                                                                  vis(hideYearText),    v=>{hideYearText=v==='hidden';     GM_setValue('hideYearText',hideYearText);      applyYearTextState();});
            B('Hide Ragdoll Engine', 'hideRagdoll',     [['shown','Shown'],['hidden','Hidden']],                                                                                                                  vis(hideRagdoll),     v=>{hideRagdoll=v==='hidden';      GM_setValue('hideRagdoll',hideRagdoll);        applyRagdollState();});
            B('Merge TIX with ROBUX','mergeTix',        [['disabled','Disabled'],['enabled','Enabled']],                                                                                                          tog(mergeTix),        v=>{mergeTix=v==='enabled';        GM_setValue('mergeTix',mergeTix);              applyTixCSS(); if(!mergeTix){revertTixItemBalance();isOnTixItemPage=false;} const r=document.querySelector('[class*="robuxContainer"]'); if(r)delete r.dataset.pekTix; if(mergeTix)attachTixListener();});
            B('i havent thought of a name for this one yet',       'siteAccuracy',    [['disabled','Disabled'],['enabled','Enabled']],                                                                                                          tog(siteAccuracy),    v=>{siteAccuracy=v==='enabled';    GM_setValue('siteAccuracy',siteAccuracy);      applySiteAccuracy();});
            B('idgaf bout trading',  'noTrading',       [['disabled','Disabled'],['enabled','Enabled']],                                                                                                          tog(noTrading),       v=>{noTrading=v==='enabled';       GM_setValue('noTrading',noTrading);            applyNoTrading();});
            B('Text Play Button',    'textPlayButton',  [['disabled','Disabled'],['enabled','Enabled']],                                                                                                          tog(textPlayButton),  v=>{textPlayButton=v==='enabled';  GM_setValue('textPlayButton',textPlayButton);  applyTextPlayButton();});
            B('Friend Suggestions',  'friendSuggest',   [['enabled','Enabled'],['disabled','Disabled']],                                                                                                          tog(friendSuggest),   v=>{friendSuggest=v==='enabled';   GM_setValue('friendSuggest',friendSuggest);    applyFriendSuggestions();});
            B('Korone To ROBLOX',    'koroneToRoblox',  [['disabled','Disabled'],['enabled','Enabled']],                                                                                                          tog(koroneToRoblox),  v=>{koroneToRoblox=v==='enabled';  GM_setValue('koroneToRoblox',koroneToRoblox);  if(koroneToRoblox)applyKoroneToRoblox();});
            card.appendChild(makeActionButtons()); return;
        }
    }

    let pending = false;
    function onDOMChange() {
        if (pending) return; pending = true;
        requestAnimationFrame(() => {
            pending = false;
            inject(); applyLayoutExpansion(); attachTixListener();
            applyFriendSuggestions(); syncFeedTheme();
            if (mergeTix) checkTixItemPage();
            if (koroneToRoblox) applyKoroneToRoblox();
        });
    }

    function startSettingsObserver() { new MutationObserver(onDOMChange).observe(document.body, { childList: true, subtree: true }); onDOMChange(); }

    let lastPath = location.pathname;
    if (location.pathname.startsWith('/My/Account')) {
        startSettingsObserver();
    } else {
        new MutationObserver(() => {
            if (location.pathname !== lastPath) {
                lastPath = location.pathname;
                revertTixItemBalance(); isOnTixItemPage = false;
                applyAll(); removeTixDropdown();
            }
            onDOMChange();
            if (location.pathname.startsWith('/My/Account')) startSettingsObserver();
        }).observe(document.body, { childList: true, subtree: true });
    }

})();