Autodarts Pro Chat

Chat v4.2 : Fix affichage du macaron (overflow), plein écran, Emojis, Anti-doublon.

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Autodarts Pro Chat
// @name:en      Autodarts Pro Chat
// @name:de      Autodarts Pro Chat
// @name:fr      Autodarts Pro Chat
// @namespace    http://tampermonkey.net/
// @version      4.2
// @description  Chat v4.2 : Fix affichage du macaron (overflow), plein écran, Emojis, Anti-doublon.
// @description:en Improved chat: Fixed badge overflow, fullscreen memory, Emojis, Anti-duplicate.
// @description:de Verbesserter Chat: Badge-Overflow-Fix, Vollbildspeicher, Emojis, Anti-Duplikat.
// @description:fr Améliore le chat d'Autodarts avec une fenêtre redimensionnable, des messages prédéfinis, des emojis et une invisibilité totale de l'interface native.
// @author       Nevro
// @license      All Rights Reserved
// @copyright    2025, Nevro
// @match        *://*.autodarts.io/*
// @grant        GM_addStyle
// @run-at       document-idle
// @icon         https://autodarts.io/favicon.ico
// ==/UserScript==

(function() {
    'use strict';

    console.log("🚀 Autodarts Pro Chat v4.2 (Badge Fix) DÉMARRÉ !");

    const UI_ID = 'ad-chat-panel-v4';
    const STORAGE_KEY = 'ad_chat_settings_v4'; 
    const PRESETS_KEY = 'ad_chat_presets_v4';
    
    // Variables d'état
    let MY_USERNAME = "Moi";
    let KNOWN_PLAYERS = [];
    let SENT_CACHE = [];
    let WAS_FULLSCREEN = false;
    let UNREAD_COUNT = 0;

    const EMOJIS = ["🎯", "👏", "👍", "👎", "😂", "😎", "😭", "😡", "🍀", "🤝", "🍺", "🍻", "👋", "🔥", "👀", "🤷‍♂️"];

    // --- CONFIG V4 ---
    let STATE = {
        isRetracted: false,
        fontSize: 13,
        normal: {
            open: { top: '100px', left: '100px', width: '320px', height: '450px' },
            retracted: { top: '20px', left: '20px' }
        },
        fullscreen: {
            open: { top: '100px', left: '100px', width: '320px', height: '450px' },
            retracted: { top: '20px', left: '20px' }
        }
    };

    let PRESETS = [
        "Hello! 👋",
        "Good luck! 🍀",
        "Nice darts! 🎯",
        "Well played! 🤝",
        "Thanks!",
        "Non"
    ];

    // --- CSS ---
    GM_addStyle(`
        /* GHOST MODE */
        .ad-ghost-target { opacity: 0 !important; pointer-events: none !important; position: fixed !important; z-index: -9999 !important; width: 1px !important; height: 1px !important; bottom: 0 !important; right: 0 !important; }
        button:has(path[d*="M20 2H4c"]), button:has(path[d*="L2 22l4-4"]), button[aria-label="Open chat"] { opacity: 0 !important; pointer-events: none !important; position: fixed !important; z-index: -9999 !important; width: 1px !important; height: 1px !important; visibility: hidden !important; }
        .chakra-modal__content:has(button[aria-label="Send"]), .chakra-modal__content:has(button[aria-label="Insert Emoji"]) { opacity: 0 !important; pointer-events: none !important; position: fixed !important; left: -9999px !important; }
        .chakra-modal__overlay { opacity: 0 !important; pointer-events: none !important; }
        #chakra-toast-manager-bottom-right, .chakra-toast { opacity: 0 !important; pointer-events: none !important; }

        /* PANEL */
        #${UI_ID} {
            position: fixed; background: #1a202c; border: 1px solid #4a5568; border-radius: 12px; z-index: 2147483647;
            display: flex; flex-direction: column; color: white; font-family: 'Segoe UI', sans-serif;
            box-shadow: 0 10px 30px rgba(0,0,0,0.9); resize: both; 
            overflow: hidden; /* C'est lui qui coupe quand c'est ouvert, normal */
            min-width: 300px; min-height: 400px;
            transition: opacity 0.2s, width 0.3s, height 0.3s, border-radius 0.3s;
            opacity: 0; pointer-events: none; touch-action: none;
        }
        #${UI_ID}.visible { opacity: 1; pointer-events: all; }
        #${UI_ID}.animating-pos { transition: top 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), left 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), width 0.3s, height 0.3s, border-radius 0.3s !important; }

        /* RETRACTED */
        #${UI_ID}.retracted { 
            width: 60px !important; height: 60px !important; 
            min-width: 0 !important; min-height: 0 !important; 
            border-radius: 50% !important; resize: none !important; 
            background: #3182ce; border: 2px solid white; cursor: pointer; 
            /* IMPORTANT: visible pour que le macaron sorte de la bulle ! */
            overflow: visible !important; 
            box-shadow: 0 4px 10px rgba(0,0,0,0.5); 
        }
        #${UI_ID} .ad-inner-content { display: flex; flex-direction: column; height: 100%; width: 100%; }
        #${UI_ID}.retracted .ad-inner-content { display: none !important; }
        .ad-bubble-icon { display: none; width: 100%; height: 100%; align-items: center; justify-content: center; font-size: 30px; pointer-events: none; user-select: none; position: relative; }
        #${UI_ID}.retracted .ad-bubble-icon { display: flex; }

        /* BADGE NOTIFICATION */
        #ad-unread-badge { 
            display: none; position: absolute; 
            top: -6px; right: -6px; /* Déborde bien de la bulle */
            background: #e53e3e; color: white; border-radius: 50%; font-size: 13px; font-weight: bold; 
            min-width: 22px; height: 22px; line-height: 20px; text-align: center; border: 2px solid white; 
            box-shadow: 0 2px 4px rgba(0,0,0,0.4); font-family: sans-serif; padding: 0 4px; box-sizing: border-box; 
            z-index: 10;
        }
        #ad-unread-badge.show { display: block; }

        /* HEADER */
        #ad-chat-header { padding: 8px 12px; background: #2d3748; display: flex; justify-content: space-between; align-items: center; font-weight: bold; border-bottom: 1px solid #4a5568; cursor: move; user-select: none; }
        .ad-header-left { display: flex; align-items: center; gap: 8px; }
        .ad-header-btns span { cursor: pointer; padding: 4px 6px; border-radius: 4px; font-size: 14px; margin-left: 2px; }
        .ad-header-btns span:hover { background: rgba(255,255,255,0.1); color: #63b3ed; }
        .ad-header-btns span.active { color: #ed8936; background: rgba(255,255,255,0.1); }
        .ad-font-ctrl { font-size: 10px !important; opacity: 0.7; border: 1px solid #4a5568; margin: 0 1px; }
        .ad-font-ctrl:hover { opacity: 1; border-color: #63b3ed; }

        /* CONTENT */
        .ad-content-area { flex: 1; overflow-y: auto; padding: 10px; background: #171923; position: relative; scroll-behavior: smooth; }
        #ad-chat-view { display: flex; flex-direction: column; gap: 8px; min-height: 100%; }
        #ad-presets-view { display: none; flex-direction: column; gap: 5px; }
        #${UI_ID}.show-presets #ad-chat-view { display: none; }
        #${UI_ID}.show-presets #ad-presets-view { display: flex; }

        /* EMOJI PICKER */
        #ad-emoji-picker { display: none; grid-template-columns: repeat(4, 1fr); gap: 5px; padding: 10px; background: #2d3748; position: absolute; bottom: 60px; right: 10px; border-radius: 8px; box-shadow: 0 -5px 20px rgba(0,0,0,0.5); border: 1px solid #4a5568; z-index: 10; }
        #ad-emoji-picker.show { display: grid; }
        .ad-emoji-item { cursor: pointer; font-size: 20px; text-align: center; padding: 5px; border-radius: 4px; }
        .ad-emoji-item:hover { background: rgba(255,255,255,0.1); transform: scale(1.2); }

        /* PRESETS UI */
        .ad-preset-item { display: flex; align-items: center; gap: 5px; background: #2d3748; padding: 5px 10px; border-radius: 6px; border: 1px solid transparent; }
        .ad-preset-item:hover { border-color: #4a5568; background: #353f52; }
        .ad-preset-text { flex: 1; cursor: pointer; font-size: 13px; user-select: none; }
        .ad-preset-text:active { transform: scale(0.98); color: #63b3ed; }
        .ad-preset-actions { display: flex; gap: 5px; opacity: 0.5; }
        .ad-preset-item:hover .ad-preset-actions { opacity: 1; }
        .ad-preset-btn { cursor: pointer; font-size: 12px; padding: 2px 5px; }
        .ad-preset-btn:hover { color: white; background: rgba(255,255,255,0.2); border-radius: 3px; }
        .ad-preset-btn.del:hover { color: #fc8181; }
        #ad-preset-add-area { margin-top: 10px; padding-top: 10px; border-top: 1px solid #4a5568; display: flex; gap: 5px; }
        #ad-new-preset-input { flex: 1; background: #1a202c; border: 1px solid #4a5568; color: white; padding: 6px; border-radius: 4px; font-size: 12px; }
        #ad-add-preset-btn { background: #48bb78; color: white; border: none; border-radius: 4px; cursor: pointer; padding: 0 10px; font-weight: bold; }

        /* INPUT */
        #ad-chat-input-area { padding: 10px; background: #2d3748; border-top: 1px solid #4a5568; display: flex; gap: 8px; align-items: center; }
        #ad-chat-input { flex: 1; background: #1a202c; border: 1px solid #4a5568; color: white; padding: 10px; border-radius: 6px; font-size: 14px; outline: none; }
        #ad-chat-input:focus { border-color: #63b3ed; }
        #ad-emoji-btn { background: transparent; border: none; font-size: 18px; cursor: pointer; padding: 0 5px; filter: grayscale(1); transition: filter 0.2s; }
        #ad-emoji-btn:hover { filter: grayscale(0); }
        #ad-send-btn { background: #3182ce; color: white; border: none; padding: 0 15px; border-radius: 6px; cursor: pointer; font-weight: bold; height: 38px; }
        #ad-send-btn:hover { background: #2b6cb0; }

        /* MESSAGES */
        .ad-msg { padding: 6px 10px; border-radius: 8px; line-height: 1.4; max-width: 85%; word-wrap: break-word; }
        .ad-msg.left { align-self: flex-start; background: #2d3748; border-left: 3px solid #ed8936; }
        .ad-msg.right { align-self: flex-end; background: #2b6cb0; border-right: 3px solid #63b3ed; } 
        .ad-msg-header { font-size: 0.85em; margin-bottom: 3px; display: block; opacity: 0.8; }
        .ad-msg-time { margin-right: 6px; }
        .ad-msg-user { font-weight: bold; }
        .ad-msg.right .ad-msg-user { color: #bee3f8; }
    `);

    // --- LOGIC ---
    function loadData() {
        const savedState = localStorage.getItem(STORAGE_KEY) || localStorage.getItem('ad_chat_settings_v3');
        if (savedState) { 
            try { 
                let parsed = JSON.parse(savedState); 
                if (parsed.open && !parsed.normal) {
                    STATE.normal = { open: parsed.open, retracted: parsed.retracted };
                    STATE.fullscreen = { open: parsed.open, retracted: parsed.retracted };
                    STATE.fontSize = parsed.fontSize || 13;
                    STATE.isRetracted = parsed.isRetracted || false;
                } else {
                    STATE = { ...STATE, ...parsed }; 
                }
            } catch(e) {} 
        }
        const savedPresets = localStorage.getItem(PRESETS_KEY) || localStorage.getItem('ad_chat_presets_v3');
        if (savedPresets) { try { PRESETS = JSON.parse(savedPresets); } catch(e) {} }
        
        WAS_FULLSCREEN = isFullscreenMode();
    }
    
    function saveState() { localStorage.setItem(STORAGE_KEY, JSON.stringify(STATE)); }
    function savePresets() { localStorage.setItem(PRESETS_KEY, JSON.stringify(PRESETS)); }

    function isFullscreenMode() {
        return !!(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement) 
            || (window.innerWidth === screen.width && window.innerHeight === screen.height);
    }

    function clampPosition(el) {
        if (!el) return;
        const rect = el.getBoundingClientRect();
        let top = parseInt(el.style.top || 0, 10);
        let left = parseInt(el.style.left || 0, 10);
        
        if (top < 0) top = 0;
        if (left < 0) left = 0;
        if (top + rect.height > window.innerHeight) top = Math.max(0, window.innerHeight - rect.height);
        if (left + rect.width > window.innerWidth) left = Math.max(0, window.innerWidth - rect.width);
        
        el.style.top = top + 'px';
        el.style.left = left + 'px';
    }

    function saveCurrentPosition(element) {
        if(!element) return;
        const mode = isFullscreenMode() ? 'fullscreen' : 'normal';
        
        if (STATE.isRetracted) {
            STATE[mode].retracted.top = element.style.top;
            STATE[mode].retracted.left = element.style.left;
        } else {
            STATE[mode].open.top = element.style.top;
            STATE[mode].open.left = element.style.left;
            STATE[mode].open.width = element.style.width;
            STATE[mode].open.height = element.style.height;
        }
        saveState();
    }

    function applyState(element) {
        element.classList.add('animating-pos');
        
        const mode = isFullscreenMode() ? 'fullscreen' : 'normal';
        const currentPos = STATE.isRetracted ? STATE[mode].retracted : STATE[mode].open;

        if (STATE.isRetracted) {
            element.classList.add('retracted');
            element.style.width = ''; element.style.height = '';
        } else {
            element.classList.remove('retracted');
            element.style.width = currentPos.width || '320px'; 
            element.style.height = currentPos.height || '450px';
        }
        
        element.style.top = currentPos.top || '100px'; 
        element.style.left = currentPos.left || '100px';
        element.style.bottom = 'auto'; element.style.right = 'auto';
        
        updateFontSize();
        
        setTimeout(() => {
            clampPosition(element);
            element.classList.remove('animating-pos');
        }, 300);
    }
    
    function updateFontSize() {
        const view = document.getElementById('ad-chat-view');
        if (view) view.style.fontSize = (STATE.fontSize || 13) + 'px';
    }

    function updateBadge() {
        const badge = document.getElementById('ad-unread-badge');
        if (!badge) return;
        if (UNREAD_COUNT > 0) {
            badge.innerText = UNREAD_COUNT > 99 ? '99+' : UNREAD_COUNT;
            badge.classList.add('show');
        } else {
            badge.classList.remove('show');
        }
    }

    // --- UI INIT ---
    function initUI() {
        if (document.getElementById(UI_ID)) return;
        loadData();

        const panel = document.createElement('div');
        panel.id = UI_ID;
        panel.innerHTML = `
            <div class="ad-bubble-icon">
                💬
                <span id="ad-unread-badge">0</span>
            </div>
            <div class="ad-inner-content">
                <div id="ad-chat-header">
                    <div class="ad-header-left">
                        <span>💬 Chat</span>
                        <span id="ad-font-minus" class="ad-font-ctrl" title="Font -">A-</span>
                        <span id="ad-font-plus" class="ad-font-ctrl" title="Font +">A+</span>
                    </div>
                    <div class="ad-header-btns">
                        <span id="ad-toggle-presets" title="Quick Messages">⚡</span>
                        <span id="ad-retract" title="Minimize">_</span>
                        <span id="ad-clear" title="Clear">🗑️</span>
                    </div>
                </div>
                <div class="ad-content-area">
                    <div id="ad-chat-view"></div>
                    <div id="ad-presets-view">
                        <div id="ad-presets-list"></div>
                        <div id="ad-preset-add-area">
                            <input type="text" id="ad-new-preset-input" placeholder="New message...">
                            <button id="ad-add-preset-btn">+</button>
                        </div>
                    </div>
                </div>
                
                <div id="ad-emoji-picker"></div>

                <div id="ad-chat-input-area">
                    <input type="text" id="ad-chat-input" placeholder="Message..." autocomplete="off">
                    <button id="ad-emoji-btn" title="Emojis">😀</button>
                    <button id="ad-send-btn">➤</button>
                </div>
            </div>
        `;
        document.body.appendChild(panel);
        applyState(panel);
        renderPresets();
        renderEmojis();

        // LISTENERS
        window.addEventListener('resize', () => {
            const currentlyFullscreen = isFullscreenMode();
            const el = document.getElementById(UI_ID);
            if (!el) return;

            if (WAS_FULLSCREEN !== currentlyFullscreen) {
                WAS_FULLSCREEN = currentlyFullscreen;
                applyState(el);
            } else {
                clampPosition(el);
                saveCurrentPosition(el);
            }
        });

        document.getElementById('ad-font-minus').onclick = (e) => { e.stopPropagation(); STATE.fontSize = Math.max(10, (STATE.fontSize||13) - 1); saveState(); updateFontSize(); };
        document.getElementById('ad-font-plus').onclick = (e) => { e.stopPropagation(); STATE.fontSize = Math.min(24, (STATE.fontSize||13) + 1); saveState(); updateFontSize(); };
        const btnPresets = document.getElementById('ad-toggle-presets');
        btnPresets.onclick = (e) => { e.stopPropagation(); panel.classList.toggle('show-presets'); btnPresets.classList.toggle('active'); };
        
        const addPresetBtn = document.getElementById('ad-add-preset-btn');
        const newPresetInput = document.getElementById('ad-new-preset-input');
        const addPreset = () => { const txt = newPresetInput.value.trim(); if (txt) { PRESETS.push(txt); savePresets(); renderPresets(); newPresetInput.value = ''; } };
        addPresetBtn.onclick = addPreset; newPresetInput.addEventListener('keypress', (e) => { if(e.key === 'Enter') addPreset(); });

        const toggleRetract = () => {
            const rect = panel.getBoundingClientRect();
            const mode = isFullscreenMode() ? 'fullscreen' : 'normal';
            if (STATE.isRetracted) { 
                STATE[mode].retracted.top = rect.top + 'px'; 
                STATE[mode].retracted.left = rect.left + 'px'; 
            } else { 
                STATE[mode].open.top = rect.top + 'px'; 
                STATE[mode].open.left = rect.left + 'px'; 
                STATE[mode].open.width = panel.style.width; 
                STATE[mode].open.height = panel.style.height; 
            }
            STATE.isRetracted = !STATE.isRetracted; 
            
            if (!STATE.isRetracted) {
                UNREAD_COUNT = 0; // Remise à zéro du compteur quand on ouvre
                updateBadge();
            }

            saveState(); 
            applyState(panel);
        };
        document.getElementById('ad-retract').onclick = (e) => { e.stopPropagation(); toggleRetract(); };
        panel.addEventListener('click', (e) => { if (panel.classList.contains('retracted')) toggleRetract(); });

        panel.addEventListener('mouseup', () => { saveCurrentPosition(panel); clampPosition(panel); }); 
        panel.addEventListener('touchend', () => { saveCurrentPosition(panel); clampPosition(panel); });

        document.getElementById('ad-clear').onclick = () => document.getElementById('ad-chat-view').innerHTML = '';
        
        const emojiBtn = document.getElementById('ad-emoji-btn');
        const emojiPicker = document.getElementById('ad-emoji-picker');
        emojiBtn.onclick = (e) => { e.stopPropagation(); emojiPicker.classList.toggle('show'); };
        document.addEventListener('click', (e) => { if(!e.target.closest('#ad-emoji-picker') && !e.target.closest('#ad-emoji-btn')) emojiPicker.classList.remove('show'); });

        const input = document.getElementById('ad-chat-input');
        const btn = document.getElementById('ad-send-btn');
        const triggerSend = () => { if(input.value.trim()) performSend(input.value.trim()); };
        input.addEventListener('keypress', (e) => { if (e.key === 'Enter') triggerSend(); }); btn.addEventListener('click', triggerSend); btn.addEventListener('touchend', (e) => { e.preventDefault(); triggerSend(); });

        makeDraggable(panel);
        updateBadge();
    }

    function renderEmojis() {
        const picker = document.getElementById('ad-emoji-picker');
        picker.innerHTML = EMOJIS.map(e => `<div class="ad-emoji-item">${e}</div>`).join('');
        picker.querySelectorAll('.ad-emoji-item').forEach(item => {
            item.onclick = () => {
                const input = document.getElementById('ad-chat-input');
                input.value += item.innerText;
                input.focus();
                picker.classList.remove('show');
            };
        });
    }

    function renderPresets() {
        const list = document.getElementById('ad-presets-list'); list.innerHTML = '';
        PRESETS.forEach((txt, index) => {
            const row = document.createElement('div'); row.className = 'ad-preset-item';
            const textSpan = document.createElement('span'); textSpan.className = 'ad-preset-text'; textSpan.textContent = txt;
            textSpan.onclick = () => {
                performSend(txt);
                document.getElementById(UI_ID).classList.remove('show-presets');
                document.getElementById('ad-toggle-presets').classList.remove('active');
            };
            const actionsDiv = document.createElement('div'); actionsDiv.className = 'ad-preset-actions';
            const editBtn = document.createElement('span'); editBtn.className = 'ad-preset-btn'; editBtn.textContent = '✏️';
            editBtn.onclick = (e) => { e.stopPropagation(); const newTxt = prompt("Edit:", txt); if (newTxt!==null) { PRESETS[index] = newTxt.trim() || txt; savePresets(); renderPresets(); } };
            const delBtn = document.createElement('span'); delBtn.className = 'ad-preset-btn del'; delBtn.textContent = '✖';
            delBtn.onclick = (e) => { e.stopPropagation(); if (confirm('Delete?')) { PRESETS.splice(index, 1); savePresets(); renderPresets(); } };
            actionsDiv.appendChild(editBtn); actionsDiv.appendChild(delBtn); row.appendChild(textSpan); row.appendChild(actionsDiv); list.appendChild(row);
        });
    }

    function makeDraggable(element) {
        let pos1=0,pos2=0,pos3=0,pos4=0; element.onmousedown=dragStart; element.addEventListener('touchstart',dragStart,{passive:false});
        function dragStart(e) {
            let target=e.target;
            if(!element.classList.contains('retracted')){if(!target.closest('#ad-chat-header'))return;}
            if(target.closest('.ad-header-btns')||target.closest('.ad-font-ctrl')||target.closest('#ad-chat-input-area')||target.closest('#ad-emoji-picker')||target.closest('.ad-content-area'))return;
            if(e.type==='touchstart'){pos3=e.touches[0].clientX;pos4=e.touches[0].clientY;}else{e.preventDefault();pos3=e.clientX;pos4=e.clientY;}
            document.onmouseup=closeDragElement;document.onmousemove=elementDrag;document.addEventListener('touchend',closeDragElement);document.addEventListener('touchmove',elementDrag,{passive:false});
        }
        function elementDrag(e) {
            e.preventDefault();
            let clientX=e.type==='touchmove'?e.touches[0].clientX:e.clientX; let clientY=e.type==='touchmove'?e.touches[0].clientY:e.clientY;
            pos1=pos3-clientX;pos2=pos4-clientY;pos3=clientX;pos4=clientY;
            
            let newTop = element.offsetTop - pos2;
            let newLeft = element.offsetLeft - pos1;

            if (newTop < 0) newTop = 0;
            if (newLeft < 0) newLeft = 0;
            if (newTop + element.offsetHeight > window.innerHeight) newTop = window.innerHeight - element.offsetHeight;
            if (newLeft + element.offsetWidth > window.innerWidth) newLeft = window.innerWidth - element.offsetWidth;

            element.style.top = newTop + "px";
            element.style.left = newLeft + "px";
            element.style.bottom = 'auto'; element.style.right = 'auto';
        }
        function closeDragElement() {
            document.onmouseup=null;document.onmousemove=null;document.removeEventListener('touchend',closeDragElement);document.removeEventListener('touchmove',elementDrag);
            saveCurrentPosition(element);
        }
    }

    // --- SCANNER JOUEURS ---
    function scanPlayers() {
        const currentPlayers = [];
        const selectors = [".ad-ext-player-name p", ".ad-ext-player-name", ".chakra-avatar__badge + span"];
        const nameElements = document.querySelectorAll(selectors.join(", "));
        nameElements.forEach(el => {
            const name = el.innerText.trim();
            if (name && !currentPlayers.includes(name)) currentPlayers.push(name);
        });
        if (JSON.stringify(currentPlayers) !== JSON.stringify(KNOWN_PLAYERS)) KNOWN_PLAYERS = currentPlayers;
        
        const menuBtn = document.querySelector('.css-xl71ch');
        if (menuBtn && menuBtn.innerText) MY_USERNAME = menuBtn.innerText.trim();
    }

    function addToCache(text) {
        SENT_CACHE.push({ text: text, time: Date.now() });
        SENT_CACHE = SENT_CACHE.filter(item => Date.now() - item.time < 3000);
    }

    function isMyOwnMessage(text) {
        SENT_CACHE = SENT_CACHE.filter(item => Date.now() - item.time < 3000);
        return SENT_CACHE.some(item => text.includes(item.text) || item.text.includes(text));
    }

    function setNativeValue(element, value) {
        const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
        const prototype = Object.getPrototypeOf(element);
        const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
        if (valueSetter && valueSetter !== prototypeValueSetter) prototypeValueSetter.call(element, value); else valueSetter.call(element, value);
        element.dispatchEvent(new Event('input', { bubbles: true })); element.dispatchEvent(new Event('change', { bubbles: true })); 
    }

    function findChatOpenButton() {
        const allButtons = Array.from(document.querySelectorAll('button'));
        let target = allButtons.find(b => b.innerHTML.includes('L2 22l4-4')) || allButtons.find(b => b.innerHTML.includes('M20 2H4c')) || allButtons.find(b => (b.getAttribute('aria-label') || "").toLowerCase().includes('chat'));
        if (target && !target.classList.contains('ad-ghost-target')) target.classList.add('ad-ghost-target');
        return target;
    }

    async function performSend(text) {
        const inputField = document.getElementById('ad-chat-input');
        const chatBtn = findChatOpenButton();
        if (!chatBtn) return;

        addToCache(text);
        addMessage("Moi", text, true);

        chatBtn.click();
        await new Promise(r => setTimeout(r, 100));
        const modalInput = document.querySelector('input[placeholder="Type your message"]') || document.querySelector('.chakra-modal__body input');
        if (modalInput) {
            setNativeValue(modalInput, text); modalInput.focus();
            await new Promise(r => setTimeout(r, 50));
            let sendBtn = document.querySelector('button[aria-label="Send"]') || modalInput.closest('form')?.querySelector('button[type="submit"]');
            if (sendBtn) {
                if (sendBtn.disabled) { modalInput.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' })); setNativeValue(modalInput, text + ' '); setNativeValue(modalInput, text); }
                await new Promise(r => setTimeout(r, 50));
                sendBtn.click(); inputField.value = '';
                setTimeout(() => document.body.dispatchEvent(new KeyboardEvent('keydown', {key: 'Escape', code: 'Escape', bubbles: true})), 50);
            }
        }
    }

    const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            mutation.addedNodes.forEach((node) => {
                if (node.id && node.id.includes('1p')) return;
                if (node.nodeType === 1) {
                    const isToast = node.classList.contains('chakra-toast') || node.querySelector('.chakra-toast');
                    if (isToast) {
                        setTimeout(() => {
                            const contentNode = node.querySelector('.chakra-toast__inner') || node;
                            const clone = contentNode.cloneNode(true);

                            let hiddenName = "";
                            const initialsEl = clone.querySelector('.chakra-avatar__initials');
                            const imgEl = clone.querySelector('img');
                            
                            if (initialsEl && initialsEl.getAttribute('aria-label')) hiddenName = initialsEl.getAttribute('aria-label');
                            else if (imgEl && imgEl.alt) hiddenName = imgEl.alt;

                            clone.querySelectorAll('.chakra-avatar__initials, .chakra-avatar, svg, img').forEach(el => el.remove());
                            let rawText = clone.innerText.trim();
                            if (!rawText) return;

                            if (isMyOwnMessage(rawText)) return;

                            let user = "Adversaire";
                            let text = rawText;

                            const sortedPlayers = [...KNOWN_PLAYERS].sort((a, b) => b.length - a.length);
                            let foundInText = false;
                            for (let player of sortedPlayers) {
                                if (rawText.toLowerCase().startsWith(player.toLowerCase())) {
                                    user = player;
                                    text = rawText.substring(player.length).trim();
                                    foundInText = true;
                                    break;
                                }
                            }

                            if (!foundInText && hiddenName) {
                                for (let player of sortedPlayers) {
                                    if (hiddenName.toLowerCase().includes(player.toLowerCase()) || player.toLowerCase().includes(hiddenName.toLowerCase())) {
                                        user = player;
                                        break;
                                    }
                                }
                            }

                            if (user === "Adversaire") {
                                const titleEl = contentNode.querySelector('.chakra-alert__title');
                                if (titleEl) user = titleEl.innerText.trim();
                            }

                            if (user.toLowerCase() === MY_USERNAME.toLowerCase()) user = "Moi";

                            if (text.includes("1Password") || text.includes("menu 1Password")) return;
                            if (text.length > 0 && !text.includes("available")) {
                                addMessage(user, text, (user === "Moi"));
                            }
                        }, 50);
                    }
                }
            });
        });
    });

    function addMessage(user, text, isMe) {
        const list = document.getElementById('ad-chat-view'); if (!list) return;
        const lastMsg = list.lastElementChild;
        if (lastMsg && lastMsg.innerText.includes(text) && (lastMsg.innerText.includes(user) || (isMe && lastMsg.classList.contains('right')))) return;
        
        const time = new Date().toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'}).replace(':', 'h');
        const div = document.createElement('div');
        div.className = `ad-msg ${isMe ? 'right' : 'left'}`;
        div.innerHTML = `<div class="ad-msg-header"><span class="ad-msg-time">${time}</span><span class="ad-msg-user">${user}</span></div><div>${text}</div>`;
        list.appendChild(div);
        
        requestAnimationFrame(() => {
            list.scrollTop = list.scrollHeight;
            const area = document.querySelector('.ad-content-area');
            if(area) area.scrollTop = area.scrollHeight;
        });

        if (!isMe && STATE.isRetracted) {
            UNREAD_COUNT++;
            updateBadge();
        }
    }

    setInterval(() => {
        initUI();
        scanPlayers(); 
        const panel = document.getElementById(UI_ID);
        if (window.location.href.includes('/matches/') && !window.location.href.includes('/history')) {
            panel.classList.add('visible');
            findChatOpenButton();
            if (!document.body.getAttribute('data-ad-chat-active')) {
                observer.observe(document.body, { childList: true, subtree: true });
                document.body.setAttribute('data-ad-chat-active', 'true');
            }
        } else {
            panel.classList.remove('visible');
        }
    }, 1000);

})();