☰

đŸ› ïž PopControl

TR — Popmundo scriptlerini tek panelden yönetmenizi sağlayan merkezi modĂŒl

Verze ze dne 10. 03. 2026. Zobrazit nejnovějơí verzi.

K instalaci tototo skriptu si budete muset nainstalovat rozơíƙení jako Tampermonkey, Greasemonkey nebo Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat rozơíƙení jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozơíƙení jako Tampermonkey nebo Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat manaĆŸer uĆŸivatelskĂœch skriptĆŻ.

(UĆŸ mĂĄm manaĆŸer uĆŸivatelskĂœch skriptĆŻ, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(UĆŸ mĂĄm manaĆŸer uĆŸivatelskĂœch stylĆŻ, nechte mě ho nainstalovat!)

// ==UserScript==
// @name        đŸ› ïž PopControl
// @namespace   popmundo.popcontrol
// @version     1.4
// @description TR — Popmundo scriptlerini tek panelden yönetmenizi sağlayan merkezi modĂŒl
// @description:en EN — Central hub to manage all Popmundo scripts from one panel
// @description:pt-BR PT-BR — Módulo central para gerenciar todos os scripts do Popmundo
// @author      luke-james-gibson
// @license     MIT
// @match       https://*.popmundo.com/*
// @run-at      document-end
// @grant       unsafeWindow
// ==/UserScript==

(function () {
'use strict';

// ─── UTILS ───────────────────────────────────────────────────────────────────
const CK = {
    get: k => { const m = document.cookie.match(new RegExp('(?:^|; )' + k + '=([^;]*)')); return m ? decodeURIComponent(m[1]) : null; },
    set: (k, v) => { document.cookie = `${k}=${encodeURIComponent(v)};domain=.popmundo.com;path=/;max-age=31536000`; }
};
const LS = {
    get: (k, d) => { try { const v = localStorage.getItem(k); return v !== null ? JSON.parse(v) : d; } catch { return d; } },
    set: (k, v) => { try { localStorage.setItem(k, JSON.stringify(v)); } catch {} },
};
const mk  = (tag, cls, txt) => { const e = document.createElement(tag); if (cls) e.className = cls; if (txt != null) e.textContent = txt; return e; };
const mkB = (txt, cls, fn)  => Object.assign(mk('button', cls, txt), { onclick: fn, type: 'button' });

// ─── LANG ────────────────────────────────────────────────────────────────────
const LANG = CK.get('ppm_lang') || 'TR';
const _D   = (tr, en, pt) => ({ TR: tr, EN: en, PT: pt }[LANG] || tr);
const STR  = {
    posLabel:    _D('Konum',            'Position',          'Posição'),
    posBottom:   _D('⬇ Alt',           '⬇ Bottom',         '⬇ Baixo'),
    posTop:      _D('⬆ Üst',           '⬆ Top',            '⬆ Cima'),
    posLeft:     _D('◀ Sol',           '◀ Left',           '◀ Esquerda'),
    posRight:    _D('▶ Sağ',           '▶ Right',          '▶ Direita'),
    langLabel:   _D('Dil',              'Language',          'Idioma'),
    scripts:     _D('Scriptler',        'Scripts',           'Scripts'),
    order:       _D('Sıra',             'Order',             'Ordem'),
    orderHint:   _D('Barda butonları sĂŒrĂŒkleyip bırakarak sıralayabilirsiniz.',
                    'Drag & drop buttons on the bar to reorder.',
                    'Arraste os botÔes na barra para reordenar.'),
    colorBar:    _D('Bar Rengi',        'Bar Color',         'Cor da Barra'),
    colorText:   _D('Yazı Rengi',       'Text Color',        'Cor do Texto'),
    colorHint:   _D('Özel renk',        'Custom color',      'Cor personalizada'),
    guide:       _D('📖 Beni Oku', '📖 Read Me', '📖 Leia-me'),
    close:       _D('Kapat',            'Close',             'Fechar'),
};

// ─── STATE ───────────────────────────────────────────────────────────────────
// ppc_enabled + ppc_order stored in cookies (cross-subdomain via .popmundo.com)
const _getEnabledObj = () => { try { return JSON.parse(decodeURIComponent(CK.get('ppc_enabled') || '%7B%7D')); } catch { return {}; } };
const _saveEnabledObj= o  => CK.set('ppc_enabled', JSON.stringify(o));
const _isEnabled  = id     => _getEnabledObj()[id] !== false;
const _setEnabled = (id,v) => { const e = _getEnabledObj(); e[id] = v; _saveEnabledObj(e); };
const _getPos     = ()     => CK.get('ppc_pos') || 'bottom';
const _setPos     = p      => CK.set('ppc_pos', p);
const _getOrder   = ()     => { try { return JSON.parse(decodeURIComponent(CK.get('ppc_order') || '%5B%5D')); } catch { return []; } };
const _setOrder   = o      => CK.set('ppc_order', JSON.stringify(o));
const _getColBg   = ()     => CK.get('ppc_bg')  || '#f5f0ff';
const _getColFg   = ()     => CK.get('ppc_fg')  || '#5a30a0';
const _setColBg   = v      => CK.set('ppc_bg', v);
const _setColFg   = v      => CK.set('ppc_fg', v);

const _registry = [];
let   _collapsed = false;

function _sortedRegistry() {
    const order = _getOrder();
    if (!order.length) return [..._registry];
    const res = [];
    order.forEach(id => { const e = _registry.find(r => r.id === id); if (e) res.push(e); });
    _registry.forEach(e => { if (!res.includes(e)) res.push(e); });
    return res;
}

// ─── COLORS ──────────────────────────────────────────────────────────────────
const PRESET_BG = [
    '#f5f0ff','#fff','#1a1035','#2c3e50','#0d1117',
    '#fff0f5','#f0fff4','#f0f8ff','#fffde7','#f3e5f5',
];
const PRESET_FG = [
    '#5a30a0','#6f42c1','#fff','#ecf0f1','#ccc',
    '#e83e8c','#28a745','#007bff','#fd7e14','#ffc107',
];

// ─── GEOMETRY ────────────────────────────────────────────────────────────────
function _mainGeom() {
    const m = document.getElementById('ppm-main') || document.getElementById('ppm-footer');
    if (!m) return { left: 0, width: window.innerWidth, right: window.innerWidth };
    const r = m.getBoundingClientRect();
    return { left: Math.round(r.left), width: m.offsetWidth, right: Math.round(r.right) };
}

// ─── PUSH STYLE ──────────────────────────────────────────────────────────────
function _applyPush(size) {
    document.getElementById('ppc-push')?.remove();
    if (!size) return;
    const s = document.createElement('style'); s.id = 'ppc-push';
    s.textContent = `#ppm-main{margin-top:${65 + size}px!important}#character-tools,#header-logo{margin-top:${size}px!important}`;
    document.head.appendChild(s);
}

// ─── BAR ─────────────────────────────────────────────────────────────────────
let _dragId   = null;
let _touchDrg = null;

function _rebuild() {
    document.getElementById('ppc-bar')?.remove();
    document.getElementById('ppc-fab')?.remove();
    document.getElementById('ppc-push')?.remove();

    const pos  = _getPos();
    const g    = _mainGeom();
    const mob  = window.innerWidth < 768;
    const vert = pos === 'left' || pos === 'right';
    const BG   = _getColBg();
    const BDR  = '1px solid ' + _shadeHex(BG, -25);
    const FG   = _getColFg();
    const SZ   = 46;  // fixed size for all modes

    // ── FAB (collapsed) ──────────────────────────────────────────────────────
    const fab = mk('button'); fab.id = 'ppc-fab'; fab.type = 'button';
    fab.textContent = '⚙';
    fab.style.cssText = [
        'position:fixed;bottom:16px;right:16px;z-index:9996',
        `background:${BG};border:${BDR};border-radius:50%`,
        `width:${SZ}px;height:${SZ}px;font-size:20px;cursor:pointer`,
        'box-shadow:0 2px 10px rgba(0,0,0,.25);display:none;align-items:center;justify-content:center',
        'font-family:inherit'
    ].join(';');
    fab.onclick = () => {
        _collapsed = false;
        fab.style.display = 'none';
        document.getElementById('ppc-bar').style.display = '';
    };
    document.body.appendChild(fab);

    // ── BAR ──────────────────────────────────────────────────────────────────
    const bar = mk('div'); bar.id = 'ppc-bar';
    bar.style.display = _collapsed ? 'none' : '';

    if (vert) {
        const side = pos === 'left' ? `left:${mob ? 0 : Math.max(0, g.left - SZ)}px` : `right:${mob ? 0 : Math.max(0, window.innerWidth - g.right - SZ)}px`;
        const shadow = pos === 'left' ? '2px 0 8px rgba(0,0,0,.12)' : '-2px 0 8px rgba(0,0,0,.12)';
        bar.style.cssText = [
            `position:fixed;${side};top:65px;bottom:0;z-index:9990`,
            `background:${BG};border:${BDR};box-shadow:${shadow}`,
            `width:${SZ}px;overflow:hidden;overflow-y:auto;scrollbar-width:none`,
            `display:flex;flex-direction:column;align-items:center;padding:4px 0;gap:0`,
        ].join(';');
    } else {
        const side   = pos === 'bottom' ? 'bottom:0' : 'top:0';
        const xPos   = mob ? 'left:0;right:0' : `left:${g.left}px;width:${g.width}px`;
        const zIdx   = pos === 'top' ? 99990 : 9990;
        const shadow = pos === 'bottom' ? '0 -2px 8px rgba(0,0,0,.12)' : '0 2px 8px rgba(0,0,0,.12)';
        bar.style.cssText = [
            `position:fixed;${xPos};${side};z-index:${zIdx}`,
            `background:${BG};border:${BDR};box-shadow:${shadow}`,
            `display:flex;flex-direction:row;flex-wrap:wrap;align-items:stretch`,
            `overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch`,
        ].join(';');
    }

    // ── BUTTON FACTORY ────────────────────────────────────────────────────────
    const BTN_W = vert ? SZ : 52;
    const mkBtn = (icon, label, onClick, draggable) => {
        const b = mk('button'); b.type = 'button';
        if (draggable) b.draggable = true;
        b.style.cssText = [
            `display:flex;flex-direction:column;align-items:center;justify-content:center`,
            `width:${BTN_W}px;min-height:${SZ}px;padding:4px 2px;gap:2px`,
            `background:none;border:none;cursor:${draggable ? 'grab' : 'pointer'};font-family:inherit;flex-shrink:0`
        ].join(';');
        const ico = mk('span'); ico.textContent = icon;
        ico.style.cssText = 'font-size:18px;line-height:1;pointer-events:none;flex-shrink:0';
        const lbl = mk('span'); lbl.textContent = label;
        lbl.style.cssText = `font-size:9px;color:${FG};font-weight:700;white-space:nowrap;pointer-events:none;max-width:${BTN_W - 4}px;overflow:hidden;text-overflow:ellipsis`;
        b.append(ico, lbl);
        b.addEventListener('mouseenter', () => b.style.background = 'rgba(0,0,0,.07)');
        b.addEventListener('mouseleave', () => b.style.background = 'none');
        b.onclick = onClick;
        return b;
    };

    const mkSep = () => {
        const d = mk('div');
        d.style.cssText = vert
            ? `height:1px;background:${_shadeHex(BG,-18)};margin:2px 6px;width:${SZ-12}px;flex-shrink:0`
            : `width:1px;background:${_shadeHex(BG,-18)};margin:5px 0;flex-shrink:0;align-self:stretch`;
        return d;
    };

    // ── DESKTOP DRAG & DROP ───────────────────────────────────────────────────
    function _attachDrag(btn, groupId) {
        btn.addEventListener('dragstart', e => {
            _dragId = groupId;
            e.dataTransfer.effectAllowed = 'move';
            setTimeout(() => btn.style.opacity = '.4', 0);
        });
        btn.addEventListener('dragend', () => { _dragId = null; btn.style.opacity = '1'; });
        btn.addEventListener('dragover', e => { e.preventDefault(); btn.style.outline = '2px dashed ' + FG; });
        btn.addEventListener('dragleave', () => btn.style.outline = '');
        btn.addEventListener('drop', e => {
            e.preventDefault(); btn.style.outline = '';
            if (!_dragId || _dragId === groupId) return;
            const order = _sortedRegistry().map(r => r.id);
            const from = order.indexOf(_dragId), to = order.indexOf(groupId);
            if (from < 0 || to < 0) return;
            order.splice(to, 0, order.splice(from, 1)[0]);
            _setOrder(order); _rebuild();
        });
    }

    // ── TOUCH DRAG (mobile) ───────────────────────────────────────────────────
    function _attachTouchDrag(container) {
        let dragEl = null, ghost = null, lastOver = null, startX = 0, startY = 0;
        const btnSel = '[data-ppc-drag]';
        container.addEventListener('touchstart', e => {
            const h = e.target.closest('[data-ppc-drag]');
            if (!h) return;
            dragEl = h; startX = e.touches[0].clientX; startY = e.touches[0].clientY;
            const r = dragEl.getBoundingClientRect();
            ghost = dragEl.cloneNode(true);
            ghost.style.cssText = `position:fixed;left:${r.left}px;top:${r.top}px;width:${r.width}px;height:${r.height}px;opacity:.65;pointer-events:none;z-index:99999;border:2px dashed ${FG};border-radius:6px;background:${BG}`;
            document.body.appendChild(ghost);
            dragEl.style.opacity = '.3';
        }, { passive: true });
        container.addEventListener('touchmove', e => {
            if (!dragEl) return;
            e.preventDefault();
            const t = e.touches[0];
            ghost.style.transform = `translate(${t.clientX - startX}px,${t.clientY - startY}px)`;
            const over = document.elementFromPoint(t.clientX, t.clientY)?.closest('[data-ppc-drag]');
            if (lastOver && lastOver !== dragEl) lastOver.style.outline = '';
            if (over && over !== dragEl) { over.style.outline = '2px dashed ' + FG; lastOver = over; }
        }, { passive: false });
        container.addEventListener('touchend', e => {
            if (!dragEl) return;
            ghost?.remove(); ghost = null;
            dragEl.style.opacity = '1';
            if (lastOver && lastOver !== dragEl) lastOver.style.outline = '';
            const t = e.changedTouches[0];
            const over = document.elementFromPoint(t.clientX, t.clientY)?.closest('[data-ppc-drag]');
            if (over && over !== dragEl) {
                const fromId = dragEl.dataset.ppcDrag, toId = over.dataset.ppcDrag;
                const order = _sortedRegistry().map(r => r.id);
                const fi = order.indexOf(fromId), ti = order.indexOf(toId);
                if (fi >= 0 && ti >= 0) { order.splice(ti, 0, order.splice(fi, 1)[0]); _setOrder(order); _rebuild(); }
            }
            dragEl = null; lastOver = null;
        }, { passive: true });
    }

    // ── COLLAPSE BUTTON ───────────────────────────────────────────────────────
    const colBtn = mk('button'); colBtn.type = 'button';
    colBtn.textContent = '▌';
    colBtn.style.cssText = [
        `font-size:9px;color:${FG};background:none;border:none;cursor:pointer`,
        `padding:2px 4px;font-family:inherit;flex-shrink:0`,
        vert ? `width:${SZ}px;text-align:center` : `min-width:20px;align-self:center`
    ].join(';');
    colBtn.title = _D('KĂŒĂ§ĂŒlt', 'Collapse', 'Recolher');
    colBtn.onclick = () => {
        _collapsed = true;
        bar.style.display = 'none';
        fab.style.display = 'flex';
    };

    // ── RENDER ENTRIES ────────────────────────────────────────────────────────
    let hasAny = false;
    _sortedRegistry().forEach(entry => {
        if (!_isEnabled(entry.id)) return;
        if (hasAny) bar.appendChild(mkSep());
        entry.buttons.forEach((btn, i) => {
            const b = mkBtn(btn.icon, btn.label, btn.onClick, i === 0);
            if (i === 0) {
                b.dataset.ppcDrag = entry.id;
                _attachDrag(b, entry.id);
            }
            bar.appendChild(b);
        });
        hasAny = true;
    });

    // ── SETTINGS + COLLAPSE ───────────────────────────────────────────────────
    if (hasAny) bar.appendChild(mkSep());
    const sBtn = mkBtn('⚙', 'PopControl', _openSettings, false);
    if (vert) { sBtn.style.marginTop = 'auto'; bar.appendChild(sBtn); bar.appendChild(mkSep()); bar.appendChild(colBtn); }
    else { sBtn.style.marginLeft = 'auto'; bar.appendChild(sBtn); bar.appendChild(colBtn); }

    document.body.appendChild(bar);

    // Attach touch drag to bar
    _attachTouchDrag(bar);

    // Push layout for top
    if (pos === 'top') _applyPush(bar.offsetHeight || 34);

    // Apply collapsed state
    if (_collapsed) { bar.style.display = 'none'; fab.style.display = 'flex'; }
}

// ─── COLOR UTILS ─────────────────────────────────────────────────────────────
function _shadeHex(hex, amt) {
    try {
        let h = hex.replace('#','');
        if (h.length === 3) h = h[0]+h[0]+h[1]+h[1]+h[2]+h[2];
        const r = Math.max(0,Math.min(255,parseInt(h.slice(0,2),16)+amt));
        const g = Math.max(0,Math.min(255,parseInt(h.slice(2,4),16)+amt));
        const b = Math.max(0,Math.min(255,parseInt(h.slice(4,6),16)+amt));
        return '#'+[r,g,b].map(x=>x.toString(16).padStart(2,'0')).join('');
    } catch { return hex; }
}

// ─── SETTINGS PANEL ──────────────────────────────────────────────────────────
function _openSettings() {
    document.getElementById('ppc-ov')?.remove();
    const pos = _getPos();

    const ov = mk('div'); ov.id = 'ppc-ov';
    ov.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:99998;display:flex;align-items:flex-end;justify-content:center';

    const box = mk('div');
    box.style.cssText = 'background:#fff;border-radius:16px 16px 0 0;padding:20px;width:100%;max-width:520px;max-height:80vh;overflow-y:auto;box-sizing:border-box';

    const mkH  = t  => { const d = mk('div'); d.textContent = t; d.style.cssText = 'font-size:10px;font-weight:700;color:#888;text-transform:uppercase;letter-spacing:.5px;margin:14px 0 6px'; return d; };
    const mkHr = () => { const hr = mk('hr'); hr.style.cssText = 'border:none;border-top:1px solid #eee;margin:10px 0'; return hr; };
    const mkPillRow = (items) => {
        const row = mk('div'); row.style.cssText = 'display:flex;gap:6px;flex-wrap:wrap';
        items.forEach(([val, lbl, active, fn]) => {
            const b = mkB(lbl, '', fn);
            b.style.cssText = `flex:1;min-width:70px;padding:7px 4px;border-radius:6px;border:1px solid ${active?'#6f42c1':'#ddd'};background:${active?'#6f42c1':'#f8f9fa'};color:${active?'#fff':'#333'};font-size:11px;cursor:pointer;font-family:inherit`;
            row.appendChild(b);
        });
        return row;
    };

    // Title row
    const titleRow = mk('div'); titleRow.style.cssText = 'display:flex;justify-content:space-between;align-items:center;margin-bottom:16px';
    titleRow.appendChild(Object.assign(mk('span'), { textContent: 'đŸ› ïž PopControl v1.4', style: { fontWeight: 'bold', fontSize: '16px' } }));
    const xBtn = mkB('✕', '', () => ov.remove()); xBtn.style.cssText = 'background:none;border:none;font-size:20px;cursor:pointer;color:#aaa;padding:0';
    titleRow.appendChild(xBtn); box.appendChild(titleRow);

    // Beni Oku button — top
    const guideBtn = mkB(STR.guide, '', () => window.open('https://rentry.org/PopControl', '_blank'));
    guideBtn.style.cssText = 'width:100%;padding:9px;border-radius:8px;background:#6f42c1;color:#fff;border:none;cursor:pointer;font-size:13px;font-weight:600;font-family:inherit;margin-bottom:10px';
    box.appendChild(guideBtn);
    box.appendChild(mkHr());

    // Position
    box.appendChild(mkH(STR.posLabel));
    box.appendChild(mkPillRow([
        ['bottom', STR.posBottom, pos==='bottom', () => { _setPos('bottom'); ov.remove(); location.reload(); }],
        ['top',    STR.posTop,    pos==='top',    () => { _setPos('top');    ov.remove(); location.reload(); }],
        ['left',   STR.posLeft,   pos==='left',   () => { _setPos('left');   ov.remove(); location.reload(); }],
        ['right',  STR.posRight,  pos==='right',  () => { _setPos('right');  ov.remove(); location.reload(); }],
    ]));
    box.appendChild(mkHr());

    // Language + Customize
    box.appendChild(mkH(STR.langLabel));
    const _hasCustom = ['ppc_lc_helper','ppc_lc_social','ppc_lc_social_mobile','ppc_lc_depot','ppc_lc_missionaid'].some(k => localStorage.getItem(k));
    box.appendChild(mkPillRow([
        ['TR','đŸ‡čđŸ‡· TĂŒrkçe', LANG==='TR', () => { CK.set('ppm_lang','TR'); ov.remove(); location.reload(); }],
        ['EN','🇬🇧 English', LANG==='EN', () => { CK.set('ppm_lang','EN'); ov.remove(); location.reload(); }],
        ['PT','đŸ‡§đŸ‡· PortuguĂȘs', LANG==='PT', () => { CK.set('ppm_lang','PT'); ov.remove(); location.reload(); }],
        ['CU','🌍 Customize', _hasCustom, () => _openCustomize(ov)],
    ]));
    box.appendChild(mkHr());

    // Color pickers
    const mkSwatches = (presets, current, onPick) => {
        const wrap = mk('div'); wrap.style.cssText = 'display:flex;flex-wrap:wrap;gap:5px;align-items:center;margin:6px 0 4px';
        let sel = current;
        const updateHexInput = () => { if (hexInp) hexInp.value = sel; };
        presets.forEach(c => {
            const sw = mk('button'); sw.type = 'button';
            sw.style.cssText = `width:22px;height:22px;border-radius:4px;border:2px solid ${c===current?'#333':'transparent'};cursor:pointer;padding:0;background:${c};flex-shrink:0;box-shadow:0 1px 3px rgba(0,0,0,.2)`;
            sw.title = c;
            sw.onclick = () => {
                sel = c;
                wrap.querySelectorAll('button[data-sw]').forEach(b => b.style.borderColor = 'transparent');
                sw.style.borderColor = '#333';
                ci.value = c; updateHexInput(); onPick(c);
            };
            sw.dataset.sw = '1';
            wrap.appendChild(sw);
        });
        const ci = mk('input'); ci.type = 'color'; ci.value = current;
        ci.style.cssText = 'width:26px;height:22px;padding:1px;border:1px solid #ccc;border-radius:4px;cursor:pointer;margin-left:2px;flex-shrink:0';
        ci.title = STR.colorHint;
        ci.oninput = () => {
            sel = ci.value;
            wrap.querySelectorAll('button[data-sw]').forEach(b => b.style.borderColor = 'transparent');
            updateHexInput(); onPick(ci.value);
        };
        wrap.appendChild(ci);
        // Hex text input
        const hexWrap = mk('div'); hexWrap.style.cssText = 'display:flex;align-items:center;gap:6px;margin-bottom:10px';
        const hexLbl = mk('span'); hexLbl.textContent = '#'; hexLbl.style.cssText = 'font-size:12px;color:#666;font-weight:600';
        const hexInp = mk('input'); hexInp.type = 'text';
        hexInp.value = current.replace('#','');
        hexInp.maxLength = 6;
        hexInp.placeholder = 'hex';
        hexInp.style.cssText = 'width:70px;padding:5px 8px;border:1px solid #ddd;border-radius:6px;font-size:12px;font-family:monospace;letter-spacing:1px';
        hexInp.oninput = () => {
            const v = '#' + hexInp.value.replace(/[^0-9a-fA-F]/g,'').slice(0,6);
            if (v.length === 7) {
                sel = v; ci.value = v;
                wrap.querySelectorAll('button[data-sw]').forEach(b => b.style.borderColor = 'transparent');
                onPick(v);
            }
        };
        hexWrap.append(hexLbl, hexInp);
        const outerWrap = mk('div');
        outerWrap.append(wrap, hexWrap);
        return outerWrap;
    };

    box.appendChild(mkH(STR.colorBar));
    box.appendChild(mkSwatches(PRESET_BG, _getColBg(), v => { _setColBg(v); _applyColors(); }));

    box.appendChild(mkH(STR.colorText));
    box.appendChild(mkSwatches(PRESET_FG, _getColFg(), v => { _setColFg(v); _applyColors(); }));
    box.appendChild(mkHr());

    // Script toggles
    if (_registry.length) {
        box.appendChild(mkH(STR.scripts));
        _sortedRegistry().forEach(entry => {
            const row = mk('div'); row.style.cssText = 'display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-bottom:1px solid #f0f0f0';
            const name = mk('span'); name.textContent = entry.icon + ' ' + entry.label; name.style.cssText = 'font-size:13px;font-weight:500';
            const swLbl = mk('label'); swLbl.style.cssText = 'position:relative;display:inline-block;width:46px;height:26px;cursor:pointer;flex-shrink:0';
            const inp   = mk('input'); inp.type = 'checkbox'; inp.checked = _isEnabled(entry.id); inp.style.cssText = 'opacity:0;width:0;height:0;position:absolute';
            const trk   = mk('span'); trk.style.cssText = `position:absolute;inset:0;border-radius:26px;background:${inp.checked?'#6f42c1':'#ccc'};transition:.25s`;
            const knob  = mk('span'); knob.style.cssText = `position:absolute;height:20px;width:20px;left:${inp.checked?'23px':'3px'};bottom:3px;background:#fff;border-radius:50%;transition:.25s;box-shadow:0 1px 3px rgba(0,0,0,.3)`;
            trk.appendChild(knob);
            inp.onchange = () => {
                const v = inp.checked;
                trk.style.background = v ? '#6f42c1' : '#ccc';
                knob.style.left = v ? '23px' : '3px';
                _setEnabled(entry.id, v);
                if (!v) entry.onUndo?.();
                _rebuild();
            };
            swLbl.append(inp, trk); row.append(name, swLbl); box.appendChild(row);
        });

        box.appendChild(mkHr());
        const hint = mk('div'); hint.textContent = STR.orderHint;
        hint.style.cssText = 'font-size:11px;color:#999;padding:4px 0 6px';
        box.appendChild(hint);
    }





    ov.onclick = e => { if (e.target === ov) ov.remove(); };
    ov.appendChild(box); document.body.appendChild(ov);
}


// ─── CUSTOMIZE (LANGUAGE IMPORT/EXPORT) ──────────────────────────────────────
function _openCustomize(parentOv) {
    const LC_KEYS = {
        helper:        'ppc_lc_helper',
        social:        'ppc_lc_social',
        social_mobile: 'ppc_lc_social_mobile',
        depot:         'ppc_lc_depot',
        missionaid:    'ppc_lc_missionaid',
    };
    const hasCustom = Object.values(LC_KEYS).some(k => localStorage.getItem(k));

    const ov = document.createElement('div');
    ov.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.75);z-index:100001;display:flex;align-items:center;justify-content:center;padding:16px;box-sizing:border-box';
    const box = document.createElement('div');
    box.style.cssText = 'background:#fff;border-radius:14px;padding:20px;width:100%;max-width:480px;max-height:90vh;overflow-y:auto;box-sizing:border-box;position:relative;font-family:inherit';

    // Header
    const hdr = document.createElement('div');
    hdr.style.cssText = 'display:flex;justify-content:space-between;align-items:center;margin-bottom:16px';
    const title = document.createElement('span');
    title.textContent = '🌍 Customize Language';
    title.style.cssText = 'font-weight:700;font-size:16px';
    const xBtn = document.createElement('button');
    xBtn.textContent = '✕'; xBtn.type = 'button';
    xBtn.style.cssText = 'background:none;border:none;font-size:20px;cursor:pointer;color:#aaa;padding:0';
    xBtn.onclick = () => ov.remove();
    hdr.append(title, xBtn);
    box.appendChild(hdr);

    // Active status
    if (hasCustom) {
        const badge = document.createElement('div');
        badge.textContent = '✅ Custom language active';
        badge.style.cssText = 'background:#d4edda;color:#155724;border-radius:6px;padding:7px 12px;font-size:12px;font-weight:600;margin-bottom:12px';
        box.appendChild(badge);
    }

    // Gemini tip
    const tip = document.createElement('div');
    tip.style.cssText = 'background:#e8f4fd;border-radius:8px;padding:10px 12px;font-size:12px;color:#1a5276;margin-bottom:14px;line-height:1.5';
    tip.innerHTML = '💡 <strong>Best results:</strong> Use <a href="https://gemini.google.com" target="_blank" style="color:#1a5276;font-weight:700">Google Gemini</a> for translation — it handles JSON format and emoji preservation reliably.';
    box.appendChild(tip);

    const mkHr = () => { const hr = document.createElement('hr'); hr.style.cssText = 'border:none;border-top:1px solid #eee;margin:14px 0'; return hr; };

    // ── EXPORT ──────────────────────────────────────────────────────────────
    box.appendChild(Object.assign(document.createElement('div'), {
        textContent: 'đŸ“€ Export',
        style: 'font-weight:700;font-size:13px;margin-bottom:6px'
    }));
    const expHint = document.createElement('div');
    expHint.textContent = 'Copies the EN strings from all connected scripts. Paste into Gemini to translate.';
    expHint.style.cssText = 'font-size:11px;color:#666;margin-bottom:8px';
    box.appendChild(expHint);

    const expBtn = document.createElement('button');
    expBtn.textContent = '📋 Copy Export JSON + Prompt';
    expBtn.type = 'button';
    expBtn.style.cssText = 'width:100%;padding:10px;background:#0d6efd;color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:600;font-family:inherit';
    expBtn.onclick = () => {
        const collected = {};
        _registry.forEach(entry => {
            if (entry.strings && Object.keys(entry.strings).length) {
                collected[entry.id] = entry.strings;
            }
        });
        if (!Object.keys(collected).length) {
            expBtn.textContent = '⚠ No scripts registered yet — navigate first';
            setTimeout(() => expBtn.textContent = '📋 Copy Export JSON + Prompt', 2500);
            return;
        }
        const promptText =
            'Translate the JSON values below to the target language.\n' +
            'Rules: keep all emojis, {n}, %s and \\n exactly as-is. Return ONLY valid JSON, no markdown, no explanation.\n\n' +
            'Target language: [ENTER TARGET LANGUAGE HERE]\n\n' +
            JSON.stringify(collected, null, 2);
        const ta = document.createElement('textarea');
        ta.value = promptText;
        document.body.appendChild(ta); ta.select(); document.execCommand('copy');
        document.body.removeChild(ta);
        navigator.clipboard?.writeText(promptText).catch(() => {});
        expBtn.textContent = '✅ Copied!';
        setTimeout(() => expBtn.textContent = '📋 Copy Export JSON + Prompt', 2500);
    };
    box.appendChild(expBtn);

    box.appendChild(mkHr());

    // ── IMPORT ──────────────────────────────────────────────────────────────
    box.appendChild(Object.assign(document.createElement('div'), {
        textContent: 'đŸ“„ Import',
        style: 'font-weight:700;font-size:13px;margin-bottom:6px'
    }));
    const impHint = document.createElement('div');
    impHint.textContent = 'Paste the translated JSON returned by Gemini (or any AI). Accepts full object or partial.';
    impHint.style.cssText = 'font-size:11px;color:#666;margin-bottom:8px';
    box.appendChild(impHint);

    const impTa = document.createElement('textarea');
    impTa.placeholder = '{ "helper": { "save": "Speichern", ... }, "social": { ... }, ... }';
    impTa.style.cssText = 'width:100%;height:160px;font-size:11px;font-family:monospace;padding:8px;border:1px solid #ddd;border-radius:6px;box-sizing:border-box;resize:vertical;margin-bottom:8px';
    box.appendChild(impTa);

    const errDiv = document.createElement('div');
    errDiv.style.cssText = 'font-size:12px;min-height:18px;margin-bottom:8px';
    box.appendChild(errDiv);

    const applyBtn = document.createElement('button');
    applyBtn.textContent = '✅ Apply & Reload';
    applyBtn.type = 'button';
    applyBtn.style.cssText = 'width:100%;padding:10px;background:#198754;color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:600;font-family:inherit;margin-bottom:8px';
    applyBtn.onclick = () => {
        try {
            const raw = impTa.value.trim();
            if (!raw) { errDiv.style.color='#dc3545'; errDiv.textContent='Paste translated JSON first.'; return; }
            const data = JSON.parse(raw);
            let applied = 0;
            Object.entries(LC_KEYS).forEach(([id, lsKey]) => {
                if (data[id] && typeof data[id] === 'object') {
                    localStorage.setItem(lsKey, JSON.stringify(data[id]));
                    applied++;
                }
            });
            if (!applied) { errDiv.style.color='#dc3545'; errDiv.textContent='No matching script keys found (helper, social, social_mobile, depot, missionaid).'; return; }
            errDiv.style.color = '#198754';
            errDiv.textContent = '✅ Applied to ' + applied + ' script(s) — reloading...';
            setTimeout(() => { ov.remove(); if (parentOv) parentOv.remove(); location.reload(); }, 1200);
        } catch (err) {
            errDiv.style.color = '#dc3545';
            errDiv.textContent = '❌ Invalid JSON: ' + err.message;
        }
    };
    box.appendChild(applyBtn);

    box.appendChild(mkHr());

    // ── RESET ───────────────────────────────────────────────────────────────
    const resetBtn = document.createElement('button');
    resetBtn.textContent = 'đŸ—‘ïž Reset to Default Language';
    resetBtn.type = 'button';
    resetBtn.style.cssText = 'width:100%;padding:8px;background:none;color:#dc3545;border:1px solid #dc3545;border-radius:8px;cursor:pointer;font-size:12px;font-family:inherit';
    resetBtn.onclick = () => {
        if (!confirm('Remove all custom language data and restore defaults?')) return;
        Object.values(LC_KEYS).forEach(k => localStorage.removeItem(k));
        ov.remove(); if (parentOv) parentOv.remove(); location.reload();
    };
    if (hasCustom) box.appendChild(resetBtn);

    ov.onclick = e => { if (e.target === ov) ov.remove(); };
    ov.appendChild(box);
    document.body.appendChild(ov);
}

// ─── APPLY COLORS (live) ─────────────────────────────────────────────────────
function _applyColors() {
    const bar = document.getElementById('ppc-bar');
    const fab = document.getElementById('ppc-fab');
    const BG  = _getColBg();
    const FG  = _getColFg();
    const BDR = '1px solid ' + _shadeHex(BG, -25);
    if (bar) {
        bar.style.background = BG;
        bar.style.border = BDR;
        bar.querySelectorAll('span:last-child').forEach(s => { if (s.style.fontSize === '9px') s.style.color = FG; });
    }
    if (fab) { fab.style.background = BG; fab.style.border = BDR; }
    // Full rebuild to repaint properly
    _rebuild();
}

// ─── PUBLIC API ───────────────────────────────────────────────────────────────
unsafeWindow.PopControl = {
    register({ id, icon, label, buttons, strings, onUndo }) {
        if (_registry.some(r => r.id === id)) return;
        _registry.push({ id, icon: icon || '🔌', label: label || id, buttons: buttons || [], strings: strings || {}, onUndo });
        _rebuild();
    },
    unregister(id) {
        const i = _registry.findIndex(r => r.id === id);
        if (i < 0) return;
        const [entry] = _registry.splice(i, 1);
        entry.onUndo?.();
        _rebuild();
    },
    isEnabled: _isEnabled,
    getLang:   () => LANG,
};

// ─── ALT+P KISAYOL ──────────────────────────────────────────────────────────
document.addEventListener('keydown', function(e) {
    if (e.altKey && (e.key === 'p' || e.key === 'P')) {
        e.preventDefault();
        const bar = document.getElementById('ppc-bar');
        const fab = document.getElementById('ppc-fab');
        if (!bar) return;
        if (_collapsed) {
            // Göster
            _collapsed = false;
            bar.style.display = '';
            if (fab) fab.style.display = 'none';
        } else {
            // Gizle — FAB gösterme, tamamen gizle
            _collapsed = true;
            bar.style.display = 'none';
            if (fab) fab.style.display = 'none';
        }
    }
});

// ─── INIT ────────────────────────────────────────────────────────────────────
(function _init() {
    const go = () => _rebuild();
    if (document.getElementById('ppm-main') || document.getElementById('ppm-footer')) { go(); return; }
    const obs = new MutationObserver(() => {
        if (document.getElementById('ppm-main') || document.getElementById('ppm-footer')) { obs.disconnect(); go(); }
    });
    obs.observe(document.body, { childList: true, subtree: true });
    setTimeout(() => { obs.disconnect(); go(); }, 4000);
})();

})();