Zepp Bot Pixelplace

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

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

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

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==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();
    });
})();