🖼️🎨 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)

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

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

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

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

})();