Greasy Fork is available in English.

🖼️🎨 Lichess.org Themes & Interface

🎨 Transforme o Lichess.org em uma experiência única! 🖼️ Crie tabuleiros incríveis, use vídeos e imagens de fundo, personalize cores, efeitos, transparência e deixe seu xadrez com a sua identidade. 🚀✨ (funciona melhor em firefox)

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

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

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu betiği yüklemek için bir betik yöneticisi eklentisi yüklemeniz gerekecektir.

(Zaten bir betik yöneticim var, hadi yükleyelim!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         🖼️🎨 Lichess.org Themes & Interface
// @namespace    http://tampermonkey.net/
// @version      10.0
// @description  🎨 Transforme o Lichess.org em uma experiência única! 🖼️ Crie tabuleiros incríveis, use vídeos e imagens de fundo, personalize cores, efeitos, transparência e deixe seu xadrez com a sua identidade. 🚀✨ (funciona melhor em firefox)
// @match        *://lichess.org/*
// @match        *://*.lichess.org/*
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    let currentBlobUrl = null;
    let neonCursorEl = null;
    let neonEnabled = GM_getValue('lichess_custom_neon_enabled', false);
    let neonColor = GM_getValue('lichess_custom_neon_color', '#39FF14');

    // Imagens nativas carregadas direto do GitHub (Formato Raw)
    const defaultBoards = [
        "https://raw.githubusercontent.com/dasfar45/Chessboard-images/main/1.jpg",
        "https://raw.githubusercontent.com/dasfar45/Chessboard-images/main/14.png",
        "https://raw.githubusercontent.com/dasfar45/Chessboard-images/main/15.png",
        "https://raw.githubusercontent.com/dasfar45/Chessboard-images/main/19.png",
        "https://raw.githubusercontent.com/dasfar45/Chessboard-images/main/20.png",
        "https://raw.githubusercontent.com/dasfar45/Chessboard-images/main/25.png",
        "https://raw.githubusercontent.com/dasfar45/Chessboard-images/main/33.png",
        "https://raw.githubusercontent.com/dasfar45/Chessboard-images/main/34.png",
        "https://raw.githubusercontent.com/dasfar45/Chessboard-images/main/38.png",
        "https://raw.githubusercontent.com/dasfar45/Chessboard-images/main/40.png",
        "https://raw.githubusercontent.com/dasfar45/Chessboard-images/main/41.png",
        "https://raw.githubusercontent.com/dasfar45/Chessboard-images/main/42.png"
    ];

    // 1. Converte o Base64 pesado em um Link Curto (Blob)
    function base64ToBlobUrl(base64) {
        const parts = base64.split(',');
        const mime = parts[0].match(/:(.*?);/)[1];
        const bstr = atob(parts[1]);
        let n = bstr.length;
        const u8arr = new Uint8Array(n);
        while(n--) {
            u8arr[n] = bstr.charCodeAt(n);
        }
        const blob = new Blob([u8arr], {type: mime});
        return URL.createObjectURL(blob);
    }

    // Aplica o CSS Permanente de Z-Index (Separando Marcações e Setas perfeitamente)
    function injectPermanentStyles() {
        let style = document.getElementById('lichess-custom-permanent-style');
        if (!style) {
            style = document.createElement('style');
            style.id = 'lichess-custom-permanent-style';
            document.head.appendChild(style);
        }
        style.textContent = `
            .cg-wrap cg-board { z-index: auto !important; }
            cg-board piece, .cg-wrap piece { z-index: 2 !important; }

            /* Camada Original (Fica embaixo das peças, exibe apenas os círculos/marcações) */
            .cg-wrap cg-board > svg.cg-shapes { z-index: 1 !important; }
            .cg-wrap cg-board > svg.cg-shapes line,
            .cg-wrap cg-board > svg.cg-shapes marker,
            .cg-wrap cg-board > svg.cg-shapes polygon,
            .cg-wrap cg-board > svg.cg-shapes path { display: none !important; }

            /* Camada Clone Topo (Fica acima das peças, exibe apenas as setas) */
            #lichess-custom-arrows-layer { z-index: 4 !important; }
            #lichess-custom-arrows-layer circle { display: none !important; }
        `;
    }

    // 2. Aplica a imagem na camada de fundo exata do Lichess
    function applyBoardImage(source) {
        let styleEl = document.getElementById('lichess-custom-board-force-style');
        if (!styleEl) {
            styleEl = document.createElement('style');
            styleEl.id = 'lichess-custom-board-force-style';
            document.head.appendChild(styleEl);
        }

        if (source) {
            let finalUrl = source;
            if (source.startsWith('data:image')) {
                if (currentBlobUrl) URL.revokeObjectURL(currentBlobUrl);
                currentBlobUrl = base64ToBlobUrl(source);
                finalUrl = currentBlobUrl;
            } else {
                if (currentBlobUrl) {
                    URL.revokeObjectURL(currentBlobUrl);
                    currentBlobUrl = null;
                }
            }

            styleEl.textContent = `
                .is2d cg-board::before, .cg-wrap cg-board::before {
                    background-image: url("${finalUrl}") !important;
                    background-size: 100% 100% !important;
                    background-repeat: no-repeat !important;
                    background-position: center !important;
                    opacity: 1 !important;
                    z-index: -1 !important;
                    pointer-events: none !important;
                }
                .is2d cg-board, .cg-wrap cg-board {
                    background-color: transparent !important;
                }
            `;
        } else {
            styleEl.textContent = '';
            if (currentBlobUrl) {
                URL.revokeObjectURL(currentBlobUrl);
                currentBlobUrl = null;
            }
        }
    }

    // Aplica a Opacidade das Casas Negras (Gera o grid em cima de imagens limpas)
    function applyDarkSquares(opacity, colorHex) {
        let styleEl = document.getElementById('lichess-custom-darksquares-style');
        if (!styleEl) {
            styleEl = document.createElement('style');
            styleEl.id = 'lichess-custom-darksquares-style';
            document.head.appendChild(styleEl);
        }

        if (opacity > 0) {
            const alphaHex = Math.round((opacity / 100) * 255).toString(16).padStart(2, '0');
            const finalColor = colorHex + alphaHex;

            styleEl.textContent = `
                .is2d cg-board::after, .cg-wrap cg-board::after {
                    content: '' !important;
                    position: absolute !important;
                    top: 0 !important; left: 0 !important;
                    width: 100% !important; height: 100% !important;
                    pointer-events: none !important;
                    z-index: 0 !important;
                    background-size: 25% 25% !important;
                    background-image: conic-gradient(${finalColor} 90deg, transparent 90deg 180deg, ${finalColor} 180deg 270deg, transparent 270deg) !important;
                }
            `;
        } else {
            styleEl.textContent = '';
        }
    }

    // Aplica os filtros de imagem em tempo real
    function applyFilters(b, s, c, h) {
        let styleEl = document.getElementById('lichess-custom-filters-style');
        if (!styleEl) {
            styleEl = document.createElement('style');
            styleEl.id = 'lichess-custom-filters-style';
            document.head.appendChild(styleEl);
        }
        styleEl.textContent = `
            .is2d cg-board::before, .cg-wrap cg-board::before {
                filter: brightness(${b}%) saturate(${s}%) contrast(${c}%) hue-rotate(${h}deg) !important;
            }
        `;
    }

    // Cursor Neon
    function updateNeonCursorManager() {
        if (neonEnabled) {
            if (!neonCursorEl) {
                neonCursorEl = document.createElement('div');
                neonCursorEl.id = 'lichess-custom-neon-cursor';
                document.body.appendChild(neonCursorEl);

                document.addEventListener('mousemove', (e) => {
                    if (neonEnabled && neonCursorEl) {
                        neonCursorEl.style.transform = `translate3d(${e.clientX - 40}px, ${e.clientY - 40}px, 0)`;
                    }
                });
            }
            neonCursorEl.style.display = 'block';
            neonCursorEl.style.position = 'fixed';
            neonCursorEl.style.top = '0';
            neonCursorEl.style.left = '0';
            neonCursorEl.style.width = '80px';
            neonCursorEl.style.height = '80px';
            neonCursorEl.style.borderRadius = '50%';
            neonCursorEl.style.pointerEvents = 'none';
            neonCursorEl.style.zIndex = '9999999';
            neonCursorEl.style.background = `radial-gradient(circle, ${neonColor}99 0%, ${neonColor}50 30%, transparent 70%)`;
            neonCursorEl.style.mixBlendMode = 'screen';
            neonCursorEl.style.willChange = 'transform';
        } else {
            if (neonCursorEl) neonCursorEl.style.display = 'none';
        }
    }

    function injectSvgDefs(color) {
        let svg = document.getElementById('lichess-custom-defs');
        if (!svg) {
            svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
            svg.id = 'lichess-custom-defs';
            Object.assign(svg.style, {
                position: 'absolute', width: '0', height: '0',
                pointerEvents: 'none', zIndex: '-999'
            });
            document.body.appendChild(svg);
        }

        svg.innerHTML = `
            <defs>
                <radialGradient id="customCircleRadial" cx="50%" cy="50%" r="50%">
                    <stop offset="0%" stop-color="${color}" stop-opacity="1" />
                    <stop offset="35%" stop-color="${color}" stop-opacity="0.95" />
                    <stop offset="65%" stop-color="${color}" stop-opacity="0.6" />
                    <stop offset="85%" stop-color="${color}" stop-opacity="0.2" />
                    <stop offset="100%" stop-color="${color}" stop-opacity="0" />
                </radialGradient>
            </defs>
        `;
    }

    // 3. Aplica as cores nas Setas e Marcações
    function applyColors(arrowColor, circleColor, circleStyle) {
        let styleEl = document.getElementById('lichess-custom-shapes-style');
        if (!styleEl) {
            styleEl = document.createElement('style');
            styleEl.id = 'lichess-custom-shapes-style';
            document.head.appendChild(styleEl);
        }

        let cssRules = '';
        if (arrowColor) {
            cssRules += `
                .cg-shapes line {
                    stroke: ${arrowColor} !important;
                    opacity: 1 !important;
                }
                .cg-shapes marker path,
                .cg-shapes marker polygon,
                .cg-shapes polygon {
                    fill: ${arrowColor} !important;
                    stroke: none !important;
                    opacity: 1 !important;
                }
            `;
        }

        if (circleColor) {
            injectSvgDefs(circleColor);
            if (circleStyle === 'radial') {
                cssRules += `
                    .cg-shapes circle {
                        stroke: none !important;
                        fill: url(#customCircleRadial) !important;
                        transform-box: fill-box !important;
                        transform-origin: center !important;
                        transform: scale(1.5) !important;
                    }
                `;
            } else if (circleStyle === 'filled') {
                cssRules += `
                    .cg-shapes circle {
                        stroke: ${circleColor} !important;
                        fill: ${circleColor} !important;
                        opacity: 1 !important;
                    }
                `;
            } else {
                cssRules += `
                    .cg-shapes circle {
                        stroke: ${circleColor} !important;
                        fill: transparent !important;
                        opacity: 1 !important;
                        stroke-width: 0.15 !important;
                    }
                `;
            }
        }
        styleEl.textContent = cssRules;
    }

    // 4. Criador Dinâmico da Camada das Setas (Para colocar APENAS setas acima das peças)
    function initShapesSplitter() {
        setInterval(() => {
            const board = document.querySelector('cg-board');
            if (!board) return;
            const container = board.parentElement;

            let topSvg = document.getElementById('lichess-custom-arrows-layer');
            if (!topSvg) {
                topSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
                topSvg.id = 'lichess-custom-arrows-layer';
                topSvg.setAttribute('class', 'cg-shapes');
                Object.assign(topSvg.style, {
                    position: 'absolute', top: 0, left: 0, width: '100%', height: '100%',
                    pointerEvents: 'none', zIndex: 4
                });
                container.appendChild(topSvg);
            }

            const baseSvg = board.querySelector('svg.cg-shapes:not(#lichess-custom-arrows-layer)');
            if (baseSvg) {
                const currentViewBox = baseSvg.getAttribute('viewBox');
                if (topSvg.getAttribute('viewBox') !== currentViewBox) {
                    topSvg.setAttribute('viewBox', currentViewBox || '');
                }
                if (topSvg.innerHTML !== baseSvg.innerHTML) {
                    topSvg.innerHTML = baseSvg.innerHTML;
                }
            } else if (topSvg.innerHTML !== '') {
                topSvg.innerHTML = '';
            }
        }, 50); // Loop ultra-leve, imperceptível pro PC, que separa a visualização em tempo real
    }

    // 5. Criar a Interface (GUI)
    function createGUI() {
        const btn = document.createElement('div');
        btn.innerHTML = '🎨';
        Object.assign(btn.style, {
            position: 'fixed', top: '20px', right: '20px', width: '45px', height: '45px',
            backgroundColor: '#262421', color: 'white', borderRadius: '50%', display: 'flex',
            justifyContent: 'center', alignItems: 'center', fontSize: '20px', cursor: 'pointer',
            boxShadow: '0 4px 12px rgba(0,0,0,0.5)', zIndex: '999999', transition: 'transform 0.2s', userSelect: 'none'
        });
        btn.onmouseenter = () => btn.style.transform = 'scale(1.1)';
        btn.onmouseleave = () => btn.style.transform = 'scale(1)';

        const panel = document.createElement('div');
        Object.assign(panel.style, {
            position: 'fixed', top: '75px', right: '20px', width: '270px', backgroundColor: '#1C1C1C',
            border: '1px solid #333', borderRadius: '12px', padding: '15px', boxShadow: '0 8px 30px rgba(0,0,0,0.9)',
            zIndex: '999999', display: 'none', flexDirection: 'column', gap: '10px', fontFamily: 'system-ui, -apple-system, sans-serif',
            maxHeight: '85vh', overflowY: 'auto', overflowX: 'hidden'
        });

        const extrasContainer = document.createElement('div');
        Object.assign(extrasContainer.style, { display: 'flex', flexDirection: 'column', gap: '10px', marginBottom: '5px' });

        const neonRow = document.createElement('div');
        Object.assign(neonRow.style, { display: 'flex', justifyContent: 'space-between', alignItems: 'center' });

        const neonLabelWrap = document.createElement('div');
        Object.assign(neonLabelWrap.style, { display: 'flex', alignItems: 'center', gap: '5px' });

        const neonCheckbox = document.createElement('input');
        neonCheckbox.type = 'checkbox';
        neonCheckbox.checked = GM_getValue('lichess_custom_neon_enabled', false);
        const neonLabel = document.createElement('span');
        neonLabel.textContent = 'Neon Cursor';
        neonLabel.style.color = '#39FF14'; neonLabel.style.fontWeight = 'bold'; neonLabel.style.fontSize = '14px';

        neonLabelWrap.appendChild(neonCheckbox);
        neonLabelWrap.appendChild(neonLabel);

        const neonColorPicker = document.createElement('input');
        neonColorPicker.type = 'color';
        neonColorPicker.value = GM_getValue('lichess_custom_neon_color', '#39FF14');
        Object.assign(neonColorPicker.style, { width: '32px', height: '24px', border: 'none', cursor: 'pointer', backgroundColor: 'transparent', padding: '0' });

        neonRow.appendChild(neonLabelWrap);
        neonRow.appendChild(neonColorPicker);

        const updateNeon = () => {
            GM_setValue('lichess_custom_neon_enabled', neonCheckbox.checked);
            GM_setValue('lichess_custom_neon_color', neonColorPicker.value);
            neonEnabled = neonCheckbox.checked;
            neonColor = neonColorPicker.value;
            updateNeonCursorManager();
        };
        neonCheckbox.addEventListener('change', updateNeon);
        neonColorPicker.addEventListener('input', updateNeon);

        extrasContainer.appendChild(neonRow);

        const darkSqContainer = document.createElement('div');
        Object.assign(darkSqContainer.style, { display: 'flex', flexDirection: 'column', gap: '4px' });

        const darkSqHeader = document.createElement('div');
        Object.assign(darkSqHeader.style, { display: 'flex', justifyContent: 'space-between', alignItems: 'center' });

        const darkSqLabel = document.createElement('span');
        darkSqLabel.textContent = 'Opacidade das Casas:';
        darkSqLabel.style.color = '#ccc'; darkSqLabel.style.fontSize = '12px';

        const darkSqColorPicker = document.createElement('input');
        darkSqColorPicker.type = 'color';
        darkSqColorPicker.value = GM_getValue('lichess_custom_ds_color', '#000000');
        Object.assign(darkSqColorPicker.style, { width: '32px', height: '24px', border: 'none', cursor: 'pointer', backgroundColor: 'transparent', padding: '0' });

        darkSqHeader.appendChild(darkSqLabel);
        darkSqHeader.appendChild(darkSqColorPicker);

        const darkSqSlider = document.createElement('input');
        darkSqSlider.type = 'range';
        darkSqSlider.min = '0'; darkSqSlider.max = '100';
        darkSqSlider.value = GM_getValue('lichess_custom_ds_opacity', 0);
        Object.assign(darkSqSlider.style, { width: '100%', cursor: 'pointer' });

        const updateDarkSq = () => {
            const op = darkSqSlider.value;
            const col = darkSqColorPicker.value;
            GM_setValue('lichess_custom_ds_opacity', op);
            GM_setValue('lichess_custom_ds_color', col);
            applyDarkSquares(op, col);
        };
        darkSqSlider.addEventListener('input', updateDarkSq);
        darkSqColorPicker.addEventListener('input', updateDarkSq);

        darkSqContainer.appendChild(darkSqHeader);
        darkSqContainer.appendChild(darkSqSlider);
        extrasContainer.appendChild(darkSqContainer);

        const dividerTop = document.createElement('hr');
        Object.assign(dividerTop.style, { border: 'none', borderTop: '1px solid #444', margin: '2px 0' });

        const title = document.createElement('h3');
        title.textContent = 'Customizar Tabuleiro';
        Object.assign(title.style, { margin: '0', fontSize: '16px', fontWeight: '600', textAlign: 'center', color: '#FFFFFF' });

        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.accept = 'image/png, image/jpeg, image/jpg, image/webp';
        Object.assign(fileInput.style, {
            display: 'block', width: '92%', fontSize: '12px', color: '#E0E0E0',
            backgroundColor: '#2C2C2C', padding: '6px', borderRadius: '6px', border: '1px solid #444', cursor: 'pointer'
        });

        const galleryContainer = document.createElement('div');
        const galleryGrid = document.createElement('div');
        Object.assign(galleryGrid.style, {
            display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '6px', padding: '5px 0'
        });
        const expandBtnContainer = document.createElement('div');
        Object.assign(expandBtnContainer.style, { textAlign: 'center', marginTop: '2px' });

        galleryContainer.appendChild(galleryGrid);
        galleryContainer.appendChild(expandBtnContainer);

        let galleryExpanded = false;

        function renderGallery() {
            galleryGrid.innerHTML = '';
            expandBtnContainer.innerHTML = '';

            let userBoards = GM_getValue('lichess_custom_boards_gallery', []);
            const currentActive = GM_getValue('lichess_custom_board_img', '');

            let allBoards = [];
            defaultBoards.forEach(url => allBoards.push({ url, isUser: false }));
            userBoards.forEach((b64, idx) => allBoards.push({ url: b64, isUser: true, index: idx }));

            const visibleCount = galleryExpanded ? allBoards.length : 8;

            for (let i = 0; i < Math.min(allBoards.length, visibleCount); i++) {
                const item = allBoards[i];
                const wrap = document.createElement('div');
                Object.assign(wrap.style, { position: 'relative', width: '100%', aspectRatio: '1/1' });

                const img = document.createElement('img');
                img.src = item.url;
                Object.assign(img.style, {
                    width: '100%', height: '100%', objectFit: 'cover', borderRadius: '4px', cursor: 'pointer',
                    border: currentActive === item.url ? '2px solid #39FF14' : '2px solid transparent', transition: 'border 0.2s'
                });
                img.onclick = () => {
                    GM_setValue('lichess_custom_board_img', item.url);
                    applyBoardImage(item.url);
                    renderGallery();
                };
                wrap.appendChild(img);

                if (item.isUser) {
                    const delBtn = document.createElement('div');
                    delBtn.innerHTML = '×';
                    Object.assign(delBtn.style, {
                        position: 'absolute', top: '-4px', right: '-4px', backgroundColor: '#FF4A4A', color: 'white',
                        borderRadius: '50%', width: '14px', height: '14px', fontSize: '12px', fontWeight: 'bold', display: 'flex',
                        justifyContent: 'center', alignItems: 'center', cursor: 'pointer', boxShadow: '0 2px 4px rgba(0,0,0,0.5)'
                    });
                    delBtn.onclick = (e) => {
                        e.stopPropagation();
                        userBoards.splice(item.index, 1);
                        GM_setValue('lichess_custom_boards_gallery', userBoards);
                        if (currentActive === item.url) {
                            GM_setValue('lichess_custom_board_img', '');
                            applyBoardImage(null);
                        }
                        renderGallery();
                    };
                    wrap.appendChild(delBtn);
                }
                galleryGrid.appendChild(wrap);
            }

            if (allBoards.length > 8) {
                const toggleBtn = document.createElement('div');
                toggleBtn.textContent = galleryExpanded ? 'Ver menos ▲' : `Ver mais ${allBoards.length - 8} ▼`;
                Object.assign(toggleBtn.style, {
                    color: '#ccc', fontSize: '11px', cursor: 'pointer', fontWeight: 'bold', padding: '4px',
                    backgroundColor: '#333', borderRadius: '4px', display: 'inline-block', transition: '0.2s'
                });
                toggleBtn.onmouseenter = () => toggleBtn.style.backgroundColor = '#444';
                toggleBtn.onmouseleave = () => toggleBtn.style.backgroundColor = '#333';
                toggleBtn.onclick = () => {
                    galleryExpanded = !galleryExpanded;
                    renderGallery();
                };
                expandBtnContainer.appendChild(toggleBtn);
            }
        }

        fileInput.addEventListener('change', function(e) {
            const file = e.target.files[0];
            if (!file) return;

            const reader = new FileReader();
            reader.onload = function(event) {
                const img = new Image();
                img.onload = function() {
                    const size = Math.min(img.width, img.height);
                    const startX = (img.width - size) / 2;
                    const startY = (img.height - size) / 2;
                    const canvas = document.createElement('canvas');
                    canvas.width = 1024;
                    canvas.height = 1024;
                    const ctx = canvas.getContext('2d');
                    ctx.drawImage(img, startX, startY, size, size, 0, 0, 1024, 1024);
                    const base64 = canvas.toDataURL('image/jpeg', 0.85);

                    let currentGallery = GM_getValue('lichess_custom_boards_gallery', []);
                    if (!currentGallery.includes(base64)) {
                        currentGallery.push(base64);
                        GM_setValue('lichess_custom_boards_gallery', currentGallery);
                    }

                    GM_setValue('lichess_custom_board_img', base64);
                    applyBoardImage(base64);
                    renderGallery();
                    fileInput.value = '';
                };
                img.src = event.target.result;
            };
            reader.readAsDataURL(file);
        });

        const resetBoardBtn = document.createElement('button');
        resetBoardBtn.textContent = 'Remover Tabuleiro';
        Object.assign(resetBoardBtn.style, {
            backgroundColor: '#444', color: 'white', border: 'none', borderRadius: '6px', padding: '6px',
            fontSize: '12px', fontWeight: 'bold', cursor: 'pointer', transition: 'background-color 0.2s'
        });
        resetBoardBtn.onmouseenter = () => resetBoardBtn.style.backgroundColor = '#666';
        resetBoardBtn.onmouseleave = () => resetBoardBtn.style.backgroundColor = '#444';
        resetBoardBtn.addEventListener('click', () => {
            GM_setValue('lichess_custom_board_img', '');
            applyBoardImage(null);
            renderGallery();
        });

        const divider1 = document.createElement('hr');
        Object.assign(divider1.style, { border: 'none', borderTop: '1px solid #444', margin: '2px 0' });

        const filterControls = document.createElement('div');
        Object.assign(filterControls.style, { display: 'flex', flexDirection: 'column', gap: '5px' });

        function createSlider(labelTxt, gmKey, min, max, def) {
            const row = document.createElement('div');
            Object.assign(row.style, { display: 'flex', justifyContent: 'space-between', alignItems: 'center' });

            const lbl = document.createElement('span');
            lbl.textContent = labelTxt;
            Object.assign(lbl.style, { color: '#bbb', fontSize: '12px' });

            const slider = document.createElement('input');
            slider.type = 'range'; slider.min = min; slider.max = max;
            slider.value = GM_getValue(gmKey, def);
            Object.assign(slider.style, { width: '130px', cursor: 'pointer' });

            slider.addEventListener('input', () => {
                GM_setValue(gmKey, slider.value);
                const b = GM_getValue('lichess_custom_brightness', 100);
                const s = GM_getValue('lichess_custom_saturation', 100);
                const c = GM_getValue('lichess_custom_contrast', 100);
                const h = GM_getValue('lichess_custom_hue', 0);
                applyFilters(b, s, c, h);
            });

            row.appendChild(lbl); row.appendChild(slider);
            return row;
        }

        filterControls.appendChild(createSlider('Brilho', 'lichess_custom_brightness', 0, 200, 100));
        filterControls.appendChild(createSlider('Saturação', 'lichess_custom_saturation', 0, 200, 100));
        filterControls.appendChild(createSlider('Contraste', 'lichess_custom_contrast', 0, 200, 100));
        filterControls.appendChild(createSlider('Matiz', 'lichess_custom_hue', 0, 360, 0));

        const divider2 = document.createElement('hr');
        Object.assign(divider2.style, { border: 'none', borderTop: '1px solid #444', margin: '2px 0' });

        const colorControls = document.createElement('div');
        Object.assign(colorControls.style, { display: 'flex', flexDirection: 'column', gap: '6px' });

        const createColorRow = (text, gmKey, defVal, isSelect = false) => {
            const row = document.createElement('div');
            Object.assign(row.style, { display: 'flex', justifyContent: 'space-between', alignItems: 'center' });
            const lbl = document.createElement('span');
            lbl.textContent = text;
            Object.assign(lbl.style, { color: '#ccc', fontSize: '13px' });

            let input;
            if (isSelect) {
                input = document.createElement('select');
                Object.assign(input.style, { backgroundColor: '#2C2C2C', color: '#E0E0E0', border: '1px solid #444', borderRadius: '4px', padding: '2px', fontSize: '12px', outline: 'none' });
                input.innerHTML = `<option value="normal">Bambolê</option><option value="filled">Bola</option><option value="radial">Degradê</option>`;
                input.value = GM_getValue(gmKey, defVal);
            } else {
                input = document.createElement('input');
                input.type = 'color';
                input.value = GM_getValue(gmKey, defVal);
                Object.assign(input.style, { width: '32px', height: '24px', border: 'none', cursor: 'pointer', backgroundColor: 'transparent', padding: '0' });
            }

            row.appendChild(lbl);
            row.appendChild(input);
            return { row, input };
        };

        const arrowControl = createColorRow('Cor das Setas:', 'lichess_custom_arrow_color', '#39FF14');
        const circleControl = createColorRow('Cor da marcação:', 'lichess_custom_circle_color', '#00D2FE');
        const styleControl = createColorRow('Estilo da marcação:', 'lichess_custom_circle_style', 'radial', true);

        const updateColors = () => {
            const aColor = arrowControl.input.value;
            const cColor = circleControl.input.value;
            const cStyle = styleControl.input.value;
            GM_setValue('lichess_custom_arrow_color', aColor);
            GM_setValue('lichess_custom_circle_color', cColor);
            GM_setValue('lichess_custom_circle_style', cStyle);
            applyColors(aColor, cColor, cStyle);
        };

        arrowControl.input.addEventListener('input', updateColors);
        circleControl.input.addEventListener('input', updateColors);
        styleControl.input.addEventListener('change', updateColors);

        colorControls.appendChild(arrowControl.row);
        colorControls.appendChild(circleControl.row);
        colorControls.appendChild(styleControl.row);

        const resetColorBtn = document.createElement('button');
        resetColorBtn.textContent = 'Resetar Tudo';
        Object.assign(resetColorBtn.style, {
            backgroundColor: '#4A90E2', color: 'white', border: 'none', borderRadius: '6px',
            padding: '6px', fontSize: '12px', fontWeight: 'bold', cursor: 'pointer', marginTop: '5px'
        });
        resetColorBtn.addEventListener('click', () => {
            GM_setValue('lichess_custom_arrow_color', '#39FF14');
            GM_setValue('lichess_custom_circle_color', '#00D2FE');
            GM_setValue('lichess_custom_circle_style', 'radial');
            GM_setValue('lichess_custom_brightness', 100);
            GM_setValue('lichess_custom_saturation', 100);
            GM_setValue('lichess_custom_contrast', 100);
            GM_setValue('lichess_custom_hue', 0);
            GM_setValue('lichess_custom_ds_opacity', 0);
            GM_setValue('lichess_custom_ds_color', '#000000');
            GM_setValue('lichess_custom_neon_enabled', false);

            arrowControl.input.value = '#39FF14';
            circleControl.input.value = '#00D2FE';
            styleControl.input.value = 'radial';

            const sliders = panel.querySelectorAll('input[type="range"]');
            sliders[0].value = 0;
            sliders[1].value = 100; sliders[2].value = 100; sliders[3].value = 100; sliders[4].value = 0;

            neonCheckbox.checked = false;
            updateNeon();

            applyDarkSquares(0, '#000000');
            applyColors('#39FF14', '#00D2FE', 'radial');
            applyFilters(100, 100, 100, 0);
        });
        colorControls.appendChild(resetColorBtn);

        panel.appendChild(extrasContainer);
        panel.appendChild(dividerTop);
        panel.appendChild(title);
        panel.appendChild(fileInput);
        panel.appendChild(galleryContainer);
        panel.appendChild(resetBoardBtn);
        panel.appendChild(divider1);
        panel.appendChild(filterControls);
        panel.appendChild(divider2);
        panel.appendChild(colorControls);

        document.body.appendChild(panel);
        document.body.appendChild(btn);

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

        renderGallery();
    }

    // 6. Inicializa o script puxando as infos
    function init() {
        injectPermanentStyles();

        const savedImg = GM_getValue('lichess_custom_board_img', null);
        if (savedImg) applyBoardImage(savedImg);

        const dsOpacity = GM_getValue('lichess_custom_ds_opacity', 0);
        const dsColor = GM_getValue('lichess_custom_ds_color', '#000000');
        applyDarkSquares(dsOpacity, dsColor);

        const b = GM_getValue('lichess_custom_brightness', 100);
        const s = GM_getValue('lichess_custom_saturation', 100);
        const c = GM_getValue('lichess_custom_contrast', 100);
        const h = GM_getValue('lichess_custom_hue', 0);
        applyFilters(b, s, c, h);

        const savedArrow = GM_getValue('lichess_custom_arrow_color', '#39FF14');
        const savedCircle = GM_getValue('lichess_custom_circle_color', '#00D2FE');
        const savedStyle = GM_getValue('lichess_custom_circle_style', 'radial');
        applyColors(savedArrow, savedCircle, savedStyle);

        updateNeonCursorManager();
        createGUI();
        initShapesSplitter(); // Inicia o motorzinho que separa perfeitamente a visão
    }

    init();

})();