Zepp Bot Pixelplace

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

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 or Violentmonkey 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         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();
    });
})();