Zepp Bot Pixelplace

Hatasız Sekme Geçişli, Mini Önizlemeli Proje Geçmişi ve Uyumlu Renk Temaları

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Zepp Bot Pixelplace
// @namespace    http://tampermonkey.net/
// @version      27.1
// @description  Hatasız Sekme Geçişli, Mini Önizlemeli Proje Geçmişi ve Uyumlu Renk Temaları
// @author       Sen
// @match        https://pixelplace.io/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const PRESETS = {
        default: { 
            bg1: 'rgba(26, 27, 30, 0.85)', bg2: 'rgba(42, 43, 46, 0.95)', angle: '135', border: '#3e4044', text: '#ffffff' 
        },
        discordDark: { 
            bg1: 'rgba(49, 51, 56, 0.85)', bg2: 'rgba(43, 45, 49, 0.95)', angle: '135', border: '#1f2023', text: '#dbdee1' 
        },
        cyberpunkNeon: { 
            bg1: 'rgba(18, 0, 36, 0.85)', bg2: 'rgba(64, 0, 48, 0.95)', angle: '135', border: '#ff007f', text: '#00ffff' 
        },
        sunsetVibe: {
            bg1: 'rgba(60, 20, 50, 0.85)', bg2: 'rgba(30, 10, 40, 0.95)', angle: '135', border: '#ff66b2', text: '#ffccf9' 
        },
        matrixTerminal: { 
            bg1: 'rgba(3, 20, 3, 0.9)', bg2: 'rgba(6, 41, 6, 0.95)', angle: '135', border: '#00ff00', text: '#00ff00' 
        },
        oceanAbyss: { 
            bg1: 'rgba(1, 22, 39, 0.85)', bg2: 'rgba(46, 196, 182, 0.85)', angle: '135', border: '#011627', text: '#fdfffc' 
        },
        emeraldForest: { 
            bg1: 'rgba(27, 67, 50, 0.85)', bg2: 'rgba(149, 213, 178, 0.85)', angle: '135', border: '#081c15', text: '#d8f3dc' 
        },
        deepCosmos: {
            bg1: 'rgba(15, 5, 30, 0.85)', bg2: 'rgba(35, 10, 60, 0.95)', angle: '135', border: '#9933ff', text: '#ffffff' 
        },
        vaporwaveSynth: {
            bg1: 'rgba(50, 10, 60, 0.85)', bg2: 'rgba(10, 40, 60, 0.95)', angle: '135', border: '#ff71ce', text: '#01cdfe' 
        },
        sakuraBlossom: {
            bg1: 'rgba(50, 20, 30, 0.85)', bg2: 'rgba(70, 30, 45, 0.95)', angle: '135', border: '#ff99bb', text: '#fff0f5' 
        },
        custom: { 
            bg1: 'rgba(26, 27, 30, 0.85)', bg2: 'rgba(42, 43, 46, 0.95)', angle: '135', border: '#3e4044', text: '#ffffff' 
        }
    };

    const PIXELPLACE_PALETTE = [
        '#ffffff', '#c4c4c4', '#888888', '#555555', '#222222', '#000000',
        '#003638', '#006600', '#477050', '#22b14c', '#02be01', '#51e119',
        '#94e044', '#98fb98', '#fbff5b', '#e5d900', '#e6be0c', '#e59500',
        '#ff7000', '#ff3904', '#e50000', '#ce2939', '#ff416a', '#9f0000',
        '#6b0000', '#ff755f', '#a06a42', '#633c1f', '#99530d', '#bb4f00',
        '#ffc49f', '#ffdfcc', '#ffa7d1', '#cf6ee4', '#7d26cd', '#ec08ec',
        '#820080', '#330077', '#020763', '#5100ff', '#0000ea', '#044bff',
        '#005ba1', '#6583cf', '#36baff', '#0083c7', '#00d3dd', '#45ffc8', '#b5e8ee'
    ];

    let scaleX = 1;
    let rotationDeg = 0;
    let extractedColorMap = {};

    window.addEventListener('load', () => {
        const imgContainer = document.createElement('div');
        imgContainer.id = 'zepp-template-container';
        imgContainer.style.position = 'absolute';
        imgContainer.style.pointerEvents = 'none';
        imgContainer.style.zIndex = '999';
        imgContainer.style.opacity = '0.5';

        const displayImg = document.createElement('img');
        displayImg.style.display = 'block';
        displayImg.style.pointerEvents = 'none';
        displayImg.style.imageRendering = 'pixelated';
        displayImg.style.transformOrigin = 'center center';
        imgContainer.appendChild(displayImg);

        let originalWidth = 0;
        let originalHeight = 0;

        const attachToCanvas = () => {
            const board = document.querySelector('#board') || document.querySelector('canvas');
            if (board && board.parentElement) {
                board.parentElement.appendChild(imgContainer);
            } else {
                setTimeout(attachToCanvas, 1000);
            }
        };
        attachToCanvas();

        const mainBtn = document.createElement('button');
        mainBtn.innerText = '🎨';
        mainBtn.title = 'Zepp Bot Panelini Aç/Kapat';
        mainBtn.style.position = 'fixed';
        mainBtn.style.top = '0px';
        mainBtn.style.left = '75px';
        mainBtn.style.zIndex = '1000000';
        mainBtn.style.width = '42px';
        mainBtn.style.height = '42px';
        mainBtn.style.borderRadius = '50%';
        mainBtn.style.display = 'flex';
        mainBtn.style.alignItems = 'center';
        mainBtn.style.justifyContent = 'center';
        mainBtn.style.boxSizing = 'border-box';
        mainBtn.style.backgroundColor = '#2a2b2e';
        mainBtn.style.color = '#fff';
        mainBtn.style.border = '2px solid #3e4044';
        mainBtn.style.cursor = 'pointer';
        mainBtn.style.fontSize = '18px';
        mainBtn.style.boxShadow = '0 2px 8px rgba(0,0,0,0.5)';
        mainBtn.style.outline = 'none';

        document.body.appendChild(mainBtn);

        const savedTop = localStorage.getItem('zepp_panel_top') || '46px';
        const savedLeft = localStorage.getItem('zepp_panel_left') || '75px';

        const panel = document.createElement('div');
        panel.style.position = 'fixed';
        panel.style.top = savedTop;
        panel.style.left = savedLeft;
        panel.style.padding = '10px';
        panel.style.borderRadius = '8px';
        panel.style.zIndex = '999999';
        panel.style.fontFamily = 'sans-serif';
        panel.style.fontSize = '11px';
        panel.style.display = 'none';
        panel.style.flexDirection = 'column';
        panel.style.gap = '6px';
        panel.style.boxShadow = '0 4px 12px rgba(0,0,0,0.6)';
        panel.style.userSelect = 'none';
        panel.style.width = '265px';

        panel.style.backdropFilter = 'blur(14px) saturate(180%)';
        panel.style.webkitBackdropFilter = 'blur(14px) saturate(180%)';

        panel.innerHTML = `
            <div id="drag-header" style="cursor: move; text-align: center; padding-bottom: 4px; border-bottom: 1px solid rgba(255,255,255,0.2);">
                <b>Zepp Bot Pixelplace v27.1</b>
            </div>

            <div style="display:flex; gap:2px; background:rgba(0,0,0,0.3); padding:2px; border-radius:4px;">
                <button class="zepp-tab-btn active" data-tab="tab-main" style="flex:1; cursor:pointer; padding:4px 2px; border-radius:3px; font-size:9.5px;">🖼️ Şablon</button>
                <button class="zepp-tab-btn" data-tab="tab-history" style="flex:1; cursor:pointer; padding:4px 2px; border-radius:3px; font-size:9.5px;">📜 Geçmiş</button>
                <button class="zepp-tab-btn" data-tab="tab-filter" style="flex:1; cursor:pointer; padding:4px 2px; border-radius:3px; font-size:9.5px;">☀️ Filtre</button>
                <button class="zepp-tab-btn" data-tab="tab-palette" style="flex:1; cursor:pointer; padding:4px 2px; border-radius:3px; font-size:9.5px;">🖌️ Palet</button>
                <button class="zepp-tab-btn" data-tab="tab-theme" style="flex:1; cursor:pointer; padding:4px 2px; border-radius:3px; font-size:9.5px;">🎨 Tema</button>
            </div>

            <div id="tab-main" class="zepp-tab-content" style="display:flex; flex-direction:column; gap:6px;">
                <label style="display:flex; flex-direction:column; gap:2px;">
                    <b>Dosya Seç:</b>
                    <input type="file" id="img-file" accept="image/*" style="font-size:10px;">
                </label>
                <label style="display:flex; flex-direction:column; gap:2px;">
                    <b>Veya URL:</b>
                    <input type="text" id="img-url" placeholder="https://..." class="zepp-input" style="width:100%; box-sizing:border-box; border-radius:3px; padding:3px;">
                </label>
                <div style="display:flex; gap:6px; align-items:center;">
                    <label>X: <input type="number" id="img-x" value="0" class="zepp-input" style="width:45px; border-radius:3px;"></label>
                    <label>Y: <input type="number" id="img-y" value="0" class="zepp-input" style="width:45px; border-radius:3px;"></label>
                    <button id="btn-center" class="zepp-btn" style="cursor:pointer; padding:3px 5px; border-radius:3px; font-size:10px;">🎯 Ortala</button>
                </div>
                <div style="display:flex; gap:4px;">
                    <button id="btn-flip" class="zepp-btn" style="flex:1; cursor:pointer; padding:3px; border-radius:3px;">🪞 Aynala</button>
                    <button id="btn-rotate" class="zepp-btn" style="flex:1; cursor:pointer; padding:3px; border-radius:3px;">🔄 Döndür</button>
                </div>
                <div style="display:flex; gap:6px; align-items:center;">
                    <label>Ölçek(%): <input type="number" id="img-scale" value="100" min="1" max="1000" class="zepp-input" style="width:50px; border-radius:3px;"></label>
                    <label>Opaklık: <input type="range" id="img-opacity" min="0.1" max="1" step="0.1" value="0.5" style="width:80px;"></label>
                </div>
                <div style="display:flex; gap:4px;">
                    <button id="btn-toggle-img" class="zepp-btn" style="flex:1; cursor:pointer; padding:3px; border-radius:3px;">Göster/Gizle</button>
                    <button id="btn-export" class="zepp-btn" style="cursor:pointer; padding:3px 5px; border-radius:3px; font-size:10px;">💾 Dışa Aktar</button>
                    <button id="btn-import" class="zepp-btn" style="cursor:pointer; padding:3px 5px; border-radius:3px; font-size:10px;">📂 İçe Aktar</button>
                    <input type="file" id="import-file" style="display:none" accept=".json">
                    <button id="btn-clear-save" class="zepp-btn" style="cursor:pointer; padding:3px 5px; border-radius:3px; background:#aa3333;" title="Sıfırla">🗑️</button>
                </div>
                <div id="status-msg" style="opacity:0.7; font-size:10px;">Hazır. (Kısayol: 8/2/4/6 ve +/-)</div>
            </div>

            <div id="tab-history" class="zepp-tab-content" style="display:none; flex-direction:column; gap:6px;">
                <div style="display:flex; justify-content:space-between; align-items:center;">
                    <b>Geçmiş & Mini Önizleme</b>
                    <button id="btn-add-history" class="zepp-btn" style="cursor:pointer; padding:2px 6px; border-radius:3px; font-size:9px;" title="Şuanki konumu ve resmi kaydet">➕ Konumu Kaydet</button>
                </div>
                <div id="history-list" style="display:flex; flex-direction:column; gap:4px; max-height:140px; overflow-y:auto; padding:3px; background:rgba(0,0,0,0.2); border-radius:4px;">
                    <span style="opacity:0.6; font-size:10px;">Henüz kayıtlı proje yok.</span>
                </div>
                <button id="btn-clear-history" class="zepp-btn" style="cursor:pointer; padding:3px; border-radius:3px; background:#aa3333; font-size:10px;">🗑️ Geçmişi Temizle</button>
            </div>

            <div id="tab-filter" class="zepp-tab-content" style="display:none; flex-direction:column; gap:6px;">
                <label style="display:flex; justify-content:space-between; align-items:center;">
                    Parlaklık (%): <input type="range" id="filter-brightness" min="0" max="200" value="100" style="width:110px;">
                </label>
                <label style="display:flex; justify-content:space-between; align-items:center;">
                    Kontrast (%): <input type="range" id="filter-contrast" min="0" max="200" value="100" style="width:110px;">
                </label>
                <label style="display:flex; justify-content:space-between; align-items:center;">
                    Renk Tonu (°): <input type="range" id="filter-hue" min="0" max="360" value="0" style="width:110px;">
                </label>
                <label style="display:flex; justify-content:space-between; align-items:center; cursor:pointer;">
                    <span>Ters Çevir (Invert):</span> <input type="checkbox" id="filter-invert" style="cursor:pointer;">
                </label>
                <button id="btn-reset-filters" class="zepp-btn" style="cursor:pointer; padding:3px; border-radius:3px;">Filtreleri Sıfırla</button>
            </div>

            <div id="tab-palette" class="zepp-tab-content" style="display:none; flex-direction:column; gap:6px;">
                <div style="display:flex; justify-content:space-between; align-items:center;">
                    <span>Sıralama:</span>
                    <select id="palette-sort" class="zepp-input" style="font-size:10px; border-radius:3px; padding:2px;">
                        <option value="count">Kullanım Sıklığı</option>
                        <option value="hue">Renk Tonu (Hue)</option>
                    </select>
                </div>
                <button id="btn-copy-all" class="zepp-btn" style="cursor:pointer; padding:3px; border-radius:3px;">📋 Tüm Renkleri Kopyala</button>
                <div id="color-palette" style="display:flex; flex-wrap:wrap; gap:3px; max-height:130px; overflow-y:auto; padding:3px; background:rgba(0,0,0,0.2); border-radius:4px;">
                    <span style="opacity:0.6; font-size:10px;">Resim bekleniyor...</span>
                </div>
            </div>

            <div id="tab-theme" class="zepp-tab-content" style="display:none; flex-direction:column; gap:6px;">
                <label style="display:flex; justify-content:space-between; align-items:center;">
                    Uyumlu Geçişli Tema:
                    <select id="theme-preset" class="zepp-input" style="border-radius:3px; padding:2px; font-size:10px;">
                        <option value="default">Karanlık (Varsayılan)</option>
                        <option value="discordDark">Discord Koyu Gri</option>
                        <option value="cyberpunkNeon">🌃 Cyberpunk Neon</option>
                        <option value="sunsetVibe">🌅 Gün Batımı</option>
                        <option value="matrixTerminal">💻 Matrix Yeşil</option>
                        <option value="oceanAbyss">🌊 Okyanus Derinliği</option>
                        <option value="emeraldForest">🌲 Zümrüt Orman</option>
                        <option value="deepCosmos">🌌 Derin Kozmos</option>
                        <option value="vaporwaveSynth">🌴 Vaporwave Synth</option>
                        <option value="sakuraBlossom">🌸 Sakura Çiçeği</option>
                        <option value="custom">Özel Renkler</option>
                    </select>
                </label>

                <div style="display:flex; justify-content:space-between; align-items:center;"><span>Geçiş Rengi 1:</span><input type="color" id="theme-bg1-color" value="#1a1b1e"></div>
                <div style="display:flex; justify-content:space-between; align-items:center;"><span>Geçiş Rengi 2:</span><input type="color" id="theme-bg2-color" value="#2a2b2e"></div>
                <div style="display:flex; justify-content:space-between; align-items:center;"><span>Geçiş Açısı (°):</span><input type="number" id="theme-angle" value="135" min="0" max="360" class="zepp-input" style="width:45px; border-radius:3px;"></div>
                <div style="display:flex; justify-content:space-between; align-items:center;"><span>Yazı Rengi:</span><input type="color" id="theme-text-color" value="#ffffff"></div>
                <div style="display:flex; justify-content:space-between; align-items:center;"><span>Kenarlık:</span><input type="color" id="theme-border-color" value="#3e4044"></div>
            </div>
        `;

        document.body.appendChild(panel);

        const tabBtns = panel.querySelectorAll('.zepp-tab-btn');
        const tabContents = panel.querySelectorAll('.zepp-tab-content');

        const presetSelect = panel.querySelector('#theme-preset');
        const bg1Picker = panel.querySelector('#theme-bg1-color');
        const bg2Picker = panel.querySelector('#theme-bg2-color');
        const angleInput = panel.querySelector('#theme-angle');
        const textPicker = panel.querySelector('#theme-text-color');
        const borderPicker = panel.querySelector('#theme-border-color');

        const applyTheme = (bg1, bg2, angle, textColor, borderColor, presetKey = 'custom') => {
            let gradientBg = `linear-gradient(${angle}deg, ${bg1}, ${bg2})`;
            panel.style.background = gradientBg;
            
            panel.style.color = textColor;
            panel.style.border = `1px solid ${borderColor}`;
            mainBtn.style.backgroundColor = bg1;
            mainBtn.style.border = `2px solid ${borderColor}`;

            tabBtns.forEach(b => {
                if (b.classList.contains('active')) {
                    b.style.backgroundColor = borderColor;
                    b.style.color = textColor;
                    b.style.fontWeight = 'bold';
                } else {
                    b.style.backgroundColor = 'rgba(0,0,0,0.2)';
                    b.style.color = textColor;
                    b.style.fontWeight = 'normal';
                }
                b.style.border = `1px solid ${borderColor}`;
            });
            panel.querySelectorAll('.zepp-input').forEach(i => { i.style.background = 'rgba(0,0,0,0.3)'; i.style.color = textColor; i.style.border = `1px solid ${borderColor}`; });
            panel.querySelectorAll('.zepp-btn').forEach(b => { b.style.background = 'rgba(255,255,255,0.1)'; b.style.color = textColor; b.style.border = `1px solid ${borderColor}`; });
            
            localStorage.setItem('zepp_theme_v27', JSON.stringify({ preset: presetKey, bg1, bg2, angle, text: textColor, border: borderColor }));
        };

        let savedTheme = PRESETS.default;
        try { 
            const raw = localStorage.getItem('zepp_theme_v27'); 
            if (raw) savedTheme = JSON.parse(raw); 
        } catch(e){}

        presetSelect.value = savedTheme.preset || 'default';
        bg1Picker.value = savedTheme.bg1 || PRESETS.default.bg1;
        bg2Picker.value = savedTheme.bg2 || PRESETS.default.bg2;
        angleInput.value = savedTheme.angle || '135';
        textPicker.value = savedTheme.text || PRESETS.default.text;
        borderPicker.value = savedTheme.border || PRESETS.default.border;

        applyTheme(bg1Picker.value, bg2Picker.value, angleInput.value, textPicker.value, borderPicker.value, presetSelect.value);

        tabBtns.forEach(btn => {
            btn.addEventListener('click', () => {
                tabBtns.forEach(b => b.classList.remove('active'));
                tabContents.forEach(c => c.style.display = 'none');

                btn.classList.add('active');
                const targetTab = panel.querySelector(`#${btn.getAttribute('data-tab')}`);
                if (targetTab) targetTab.style.display = 'flex';

                applyTheme(bg1Picker.value, bg2Picker.value, angleInput.value, textPicker.value, borderPicker.value, presetSelect.value);
            });
        });

        presetSelect.addEventListener('change', (e) => {
            const theme = PRESETS[e.target.value];
            if (theme) {
                bg1Picker.value = theme.bg1; 
                bg2Picker.value = theme.bg2; 
                angleInput.value = theme.angle;
                textPicker.value = theme.text; 
                borderPicker.value = theme.border;
                applyTheme(theme.bg1, theme.bg2, theme.angle, theme.text, theme.border, e.target.value);
            }
        });

        [bg1Picker, bg2Picker, angleInput, textPicker, borderPicker].forEach(p => {
            p.addEventListener('input', () => {
                presetSelect.value = 'custom';
                applyTheme(bg1Picker.value, bg2Picker.value, angleInput.value, textPicker.value, borderPicker.value, 'custom');
            });
        });

        const fileInput = panel.querySelector('#img-file');
        const urlInput = panel.querySelector('#img-url');
        const xInput = panel.querySelector('#img-x');
        const yInput = panel.querySelector('#img-y');
        const scaleInput = panel.querySelector('#img-scale');
        const opacityInput = panel.querySelector('#img-opacity');
        const toggleImgBtn = panel.querySelector('#btn-toggle-img');
        const clearSaveBtn = panel.querySelector('#btn-clear-save');
        const flipBtn = panel.querySelector('#btn-flip');
        const rotateBtn = panel.querySelector('#btn-rotate');
        const centerBtn = panel.querySelector('#btn-center');
        const exportBtn = panel.querySelector('#btn-export');
        const importBtn = panel.querySelector('#btn-import');
        const importFile = panel.querySelector('#import-file');
        const copyAllBtn = panel.querySelector('#btn-copy-all');
        const statusMsg = panel.querySelector('#status-msg');
        const paletteContainer = panel.querySelector('#color-palette');
        const paletteSortSelect = panel.querySelector('#palette-sort');

        const historyListContainer = panel.querySelector('#history-list');
        const addHistoryBtn = panel.querySelector('#btn-add-history');
        const clearHistoryBtn = panel.querySelector('#btn-clear-history');

        const brightnessInput = panel.querySelector('#filter-brightness');
        const contrastInput = panel.querySelector('#filter-contrast');
        const hueInput = panel.querySelector('#filter-hue');
        const invertCheckbox = panel.querySelector('#filter-invert');
        const resetFiltersBtn = panel.querySelector('#btn-reset-filters');

        const updateTransforms = () => {
            displayImg.style.transform = `scaleX(${scaleX}) rotate(${rotationDeg}deg)`;
            saveTemplateData();
        };

        flipBtn.addEventListener('click', () => { scaleX *= -1; updateTransforms(); });
        rotateBtn.addEventListener('click', () => { rotationDeg = (rotationDeg + 90) % 360; updateTransforms(); });
        centerBtn.addEventListener('click', () => {
            const board = document.querySelector('#board') || document.querySelector('canvas');
            if (board) {
                const cx = Math.round((board.clientWidth - displayImg.width) / 2);
                const cy = Math.round((board.clientHeight - displayImg.height) / 2);
                xInput.value = cx >= 0 ? cx : 0;
                yInput.value = cy >= 0 ? cy : 0;
                imgContainer.style.left = `${xInput.value}px`;
                imgContainer.style.top = `${yInput.value}px`;
                saveTemplateData();
                statusMsg.innerText = "Şablon ortalandı! 🎯";
            }
        });

        window.addEventListener('keydown', (e) => {
            if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) return;
            const key = e.key;
            let currentX = parseInt(xInput.value) || 0;
            let currentY = parseInt(yInput.value) || 0;
            let moved = false;

            if (key === '8') { currentY--; moved = true; }
            if (key === '2') { currentY++; moved = true; }
            if (key === '4') { currentX--; moved = true; }
            if (key === '6') { currentX++; moved = true; }
            if (key === '0') { currentX = 0; currentY = 0; moved = true; }

            if (moved) {
                e.preventDefault();
                xInput.value = currentX; yInput.value = currentY;
                imgContainer.style.left = `${currentX}px`;
                imgContainer.style.top = `${currentY}px`;
                saveTemplateData();
            }
            if (key === '+' || key === '=') {
                let op = Math.min(1, parseFloat(opacityInput.value) + 0.1);
                opacityInput.value = op.toFixed(1); imgContainer.style.opacity = op; saveTemplateData();
            }
            if (key === '-' || key === '_') {
                let op = Math.max(0.1, parseFloat(opacityInput.value) - 0.1);
                opacityInput.value = op.toFixed(1); imgContainer.style.opacity = op; saveTemplateData();
            }
        });

        const renderHistory = () => {
            historyListContainer.innerHTML = '';
            let historyData = [];
            try {
                const raw = localStorage.getItem('zepp_history_v27');
                if (raw) historyData = JSON.parse(raw);
            } catch(e){}

            if (historyData.length === 0) {
                historyListContainer.innerHTML = '<span style="opacity:0.6; font-size:10px;">Henüz kayıtlı proje yok.</span>';
                return;
            }

            historyData.forEach((item, index) => {
                const row = document.createElement('div');
                row.style.display = 'flex';
                row.style.justifyContent = 'space-between';
                row.style.alignItems = 'center';
                row.style.padding = '3px 5px';
                row.style.background = 'rgba(255,255,255,0.05)';
                row.style.borderRadius = '3px';
                row.style.border = '1px solid rgba(255,255,255,0.1)';
                row.style.cursor = 'pointer';

                const leftWrapper = document.createElement('div');
                leftWrapper.style.display = 'flex';
                leftWrapper.style.alignItems = 'center';
                leftWrapper.style.gap = '6px';
                leftWrapper.style.flex = '1';

                const thumb = document.createElement('img');
                thumb.src = item.src || '';
                thumb.style.width = '22px';
                thumb.style.height = '22px';
                thumb.style.objectFit = 'cover';
                thumb.style.borderRadius = '2px';
                thumb.style.border = '1px solid rgba(255,255,255,0.2)';
                thumb.style.imageRendering = 'pixelated';

                const info = document.createElement('span');
                info.style.fontSize = '9.5px';
                info.innerText = `X: ${item.x}, Y: ${item.y} (${item.date || 'Proje'})`;

                leftWrapper.appendChild(thumb);
                leftWrapper.appendChild(info);

                leftWrapper.addEventListener('click', () => {
                    xInput.value = item.x;
                    yInput.value = item.y;
                    scaleInput.value = item.scale || 100;
                    opacityInput.value = item.opacity || 0.5;
                    imgContainer.style.left = `${item.x}px`;
                    imgContainer.style.top = `${item.y}px`;
                    imgContainer.style.opacity = item.opacity || 0.5;
                    scaleX = item.scaleX !== undefined ? item.scaleX : 1;
                    rotationDeg = item.rotationDeg || 0;
                    updateTransforms();
                    if (item.src) {
                        displayImg.src = item.src;
                        displayImg.style.display = 'block';
                    }
                    statusMsg.innerText = `Proje yüklendi: X:${item.x}, Y:${item.y} 🚀`;
                    saveTemplateData();
                });

                const delBtn = document.createElement('button');
                delBtn.innerText = '❌';
                delBtn.style.cursor = 'pointer';
                delBtn.style.background = 'transparent';
                delBtn.style.border = 'none';
                delBtn.style.fontSize = '9px';
                delBtn.title = 'Bu kaydı sil';
                delBtn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    historyData.splice(index, 1);
                    localStorage.setItem('zepp_history_v27', JSON.stringify(historyData));
                    renderHistory();
                });

                row.appendChild(leftWrapper);
                row.appendChild(delBtn);
                historyListContainer.appendChild(row);
            });
        };

        addHistoryBtn.addEventListener('click', () => {
            let historyData = [];
            try {
                const raw = localStorage.getItem('zepp_history_v27');
                if (raw) historyData = JSON.parse(raw);
            } catch(e){}

            const now = new Date();
            const dateStr = `${now.getHours().toString().padStart(2,'0')}:${now.getMinutes().toString().padStart(2,'0')}`;

            const newItem = {
                src: displayImg.src || '',
                x: xInput.value || '0',
                y: yInput.value || '0',
                scale: scaleInput.value || '100',
                opacity: opacityInput.value || '0.5',
                scaleX,
                rotationDeg,
                date: dateStr
            };

            historyData.unshift(newItem);
            if (historyData.length > 15) historyData.pop();
            localStorage.setItem('zepp_history_v27', JSON.stringify(historyData));
            renderHistory();
            statusMsg.innerText = "Konum ve önizleme kaydedildi! 📌";
        });

        clearHistoryBtn.addEventListener('click', () => {
            localStorage.removeItem('zepp_history_v27');
            renderHistory();
            statusMsg.innerText = "Geçmiş temizlendi.";
        });

        const applyFilters = () => {
            displayImg.style.filter = `brightness(${brightnessInput.value}%) contrast(${contrastInput.value}%) hue-rotate(${hueInput.value}deg) invert(${invertCheckbox.checked ? 100 : 0}%)`;
            saveTemplateData();
        };

        brightnessInput.addEventListener('input', applyFilters);
        contrastInput.addEventListener('input', applyFilters);
        hueInput.addEventListener('input', applyFilters);
        invertCheckbox.addEventListener('change', applyFilters);
        resetFiltersBtn.addEventListener('click', () => {
            brightnessInput.value = 100; contrastInput.value = 100; hueInput.value = 0; invertCheckbox.checked = false; applyFilters();
        });

        const hexToRgb = (hex) => {
            let bigint = parseInt(hex.replace('#', ''), 16);
            return [(bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255];
        };

        const getColorDistance = (rgb1, rgb2) => Math.sqrt(Math.pow(rgb1[0]-rgb2[0], 2) + Math.pow(rgb1[1]-rgb2[1], 2) + Math.pow(rgb1[2]-rgb2[2], 2));
        
        const findClosestPixelplaceColor = (hex) => {
            let rgb = hexToRgb(hex);
            let closest = PIXELPLACE_PALETTE[0];
            let minDist = Infinity;
            for (let pHex of PIXELPLACE_PALETTE) {
                let dist = getColorDistance(rgb, hexToRgb(pHex));
                if (dist < minDist) { minDist = dist; closest = pHex; }
            }
            return closest;
        };

        const hexToHue = (hex) => {
            let rgb = hexToRgb(hex);
            let r = rgb[0]/255, g = rgb[1]/255, b = rgb[2]/255;
            let max = Math.max(r,g,b), min = Math.min(r,g,b);
            let h, d = max - min;
            if (max === min) h = 0;
            else h = max === r ? (g - b) / d + (g < b ? 6 : 0) : max === g ? (b - r) / d + 2 : (r - g) / d + 4;
            return (h / 6) * 360;
        };

        const renderPalette = () => {
            paletteContainer.innerHTML = '';
            let colors = Object.keys(extractedColorMap);
            if (colors.length === 0) {
                paletteContainer.innerHTML = '<span style="opacity:0.6; font-size:10px;">Renk bulunamadı.</span>';
                return;
            }
            if (paletteSortSelect.value === 'count') colors.sort((a, b) => extractedColorMap[b] - extractedColorMap[a]);
            else colors.sort((a, b) => hexToHue(a) - hexToHue(b));

            colors.slice(0, 40).forEach(hex => {
                const mappedHex = findClosestPixelplaceColor(hex);
                const swatch = document.createElement('div');
                swatch.style.width = '20px'; swatch.style.height = '20px';
                swatch.style.backgroundColor = mappedHex; swatch.style.borderRadius = '3px';
                swatch.style.cursor = 'pointer'; swatch.style.border = '1px solid rgba(255,255,255,0.3)';
                swatch.title = `Orijinal: ${hex} -> PP: ${mappedHex} (${extractedColorMap[hex]} piks)`;
                swatch.addEventListener('click', () => {
                    navigator.clipboard.writeText(mappedHex);
                    swatch.style.border = '2px solid #55ff55';
                    setTimeout(() => swatch.style.border = '1px solid rgba(255,255,255,0.3)', 500);
                });
                paletteContainer.appendChild(swatch);
            });
        };

        paletteSortSelect.addEventListener('change', renderPalette);
        copyAllBtn.addEventListener('click', () => {
            let colors = Object.keys(extractedColorMap);
            if (colors.length === 0) return;
            navigator.clipboard.writeText([...new Set(colors.map(h => findClosestPixelplaceColor(h)))].join(', '));
            statusMsg.innerText = "Palet renkleri kopyalandı! 📋";
        });

        const saveTemplateData = () => {
            const data = {
                src: displayImg.src || '', x: xInput.value, y: yInput.value,
                scale: scaleInput.value, opacity: opacityInput.value,
                scaleX, rotationDeg, visible: displayImg.style.display !== 'none',
                filters: { b: brightnessInput.value, c: contrastInput.value, h: hueInput.value, inv: invertCheckbox.checked }
            };
            try { localStorage.setItem('zepp_saved_template_v27', JSON.stringify(data)); } catch(e){}
        };

        const loadSavedTemplate = () => {
            try {
                const raw = localStorage.getItem('zepp_saved_template_v27');
                if (!raw) return;
                const data = JSON.parse(raw);
                if (data.x !== undefined) { xInput.value = data.x; imgContainer.style.left = `${data.x}px`; }
                if (data.y !== undefined) { yInput.value = data.y; imgContainer.style.top = `${data.y}px`; }
                if (data.scale !== undefined) scaleInput.value = data.scale;
                if (data.opacity !== undefined) { opacityInput.value = data.opacity; imgContainer.style.opacity = data.opacity; }
                if (data.scaleX !== undefined) scaleX = data.scaleX;
                if (data.rotationDeg !== undefined) rotationDeg = data.rotationDeg;
                updateTransforms();
                if (data.filters) {
                    brightnessInput.value = data.filters.b || 100;
                    contrastInput.value = data.filters.c || 100;
                    hueInput.value = data.filters.h || 0;
                    invertCheckbox.checked = !!data.filters.inv;
                    applyFilters();
                }
                if (data.src) {
                    displayImg.src = data.src;
                    displayImg.style.display = data.visible ? 'block' : 'none';
                    statusMsg.innerText = "Kayıt yüklendi! 💾";
                }
            } catch(e){}
        };

        exportBtn.addEventListener('click', () => {
            const raw = localStorage.getItem('zepp_saved_template_v27');
            if (!raw) return;
            const blob = new Blob([raw], { type: 'application/json' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a'); a.href = url; a.download = 'zepp_backup.json'; a.click();
            URL.revokeObjectURL(url);
        });

        importBtn.addEventListener('click', () => importFile.click());
        importFile.addEventListener('change', (e) => {
            const file = e.target.files[0];
            if (!file) return;
            const reader = new FileReader();
            reader.onload = (ev) => {
                localStorage.setItem('zepp_saved_template_v27', ev.target.result);
                loadSavedTemplate();
                statusMsg.innerText = "Yedek yüklendi! ✅";
            };
            reader.readAsText(file);
        });

        const extractColors = (img) => {
            try {
                const canvas = document.createElement('canvas');
                const ctx = canvas.getContext('2d');
                canvas.width = img.naturalWidth || img.width;
                canvas.height = img.naturalHeight || img.height;
                if (!canvas.width || !canvas.height) return;
                ctx.drawImage(img, 0, 0);
                const data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
                extractedColorMap = {};
                for (let i = 0; i < data.length; i += 4) {
                    if (data[i+3] < 50) continue;
                    let hex = `#${((1<<24) + (data[i]<<16) + (data[i+1]<<8) + data[i+2]).toString(16).slice(1).toUpperCase()}`;
                    extractedColorMap[hex] = (extractedColorMap[hex] || 0) + 1;
                }
                renderPalette();
            } catch(e) {}
        };

        const dragHeader = panel.querySelector('#drag-header');
        let isDragging = false, startX, startY, initialLeft, initialTop;
        dragHeader.addEventListener('mousedown', (e) => {
            isDragging = true; startX = e.clientX; startY = e.clientY;
            initialLeft = panel.offsetLeft; initialTop = panel.offsetTop;
        });

        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                const newLeft = `${initialLeft + (e.clientX - startX)}px`;
                const newTop = `${initialTop + (e.clientY - startY)}px`;
                panel.style.left = newLeft; panel.style.top = newTop;
                localStorage.setItem('zepp_panel_left', newLeft);
                localStorage.setItem('zepp_panel_top', newTop);
            }
        });

        document.addEventListener('mouseup', () => isDragging = false);

        mainBtn.addEventListener('click', () => { panel.style.display = panel.style.display === 'none' ? 'flex' : 'none'; });

        const updateImageSize = () => {
            if (!originalWidth || !originalHeight) return;
            const scale = parseFloat(scaleInput.value) / 100;
            const newW = Math.round(originalWidth * scale);
            const newH = Math.round(originalHeight * scale);
            displayImg.style.width = `${newW}px`;
            displayImg.style.height = `${newH}px`;
            saveTemplateData();
        };

        displayImg.addEventListener('load', () => {
            originalWidth = displayImg.naturalWidth;
            originalHeight = displayImg.naturalHeight;
            updateImageSize();
            extractColors(displayImg);
            saveTemplateData();
        });

        fileInput.addEventListener('change', (e) => {
            const file = e.target.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = (ev) => {
                    displayImg.src = ev.target.result;
                    displayImg.style.display = 'block';
                    statusMsg.innerText = "Resim yüklendi! ✅";
                    saveTemplateData();
                };
                reader.readAsDataURL(file);
            }
        });

        urlInput.addEventListener('change', (e) => {
            if (e.target.value) {
                displayImg.crossOrigin = "Anonymous";
                displayImg.src = e.target.value;
                displayImg.style.display = 'block';
                statusMsg.innerText = "URL yüklendi! ✅";
                saveTemplateData();
            }
        });

        xInput.addEventListener('input', (e) => { imgContainer.style.left = `${e.target.value}px`; saveTemplateData(); });
        yInput.addEventListener('input', (e) => { imgContainer.style.top = `${e.target.value}px`; saveTemplateData(); });
        scaleInput.addEventListener('input', updateImageSize);
        opacityInput.addEventListener('input', (e) => { imgContainer.style.opacity = e.target.value; saveTemplateData(); });

        toggleImgBtn.addEventListener('click', () => {
            displayImg.style.display = displayImg.style.display === 'none' ? 'block' : 'none';
            saveTemplateData();
        });

        clearSaveBtn.addEventListener('click', () => {
            localStorage.removeItem('zepp_saved_template_v27');
            displayImg.src = ''; displayImg.style.display = 'none';
            paletteContainer.innerHTML = '<span style="opacity:0.6; font-size:10px;">Kayıt temizlendi.</span>';
            statusMsg.innerText = "Sıfırlandı.";
        });

        loadSavedTemplate();
        renderHistory();
    });
})();