Fiu Chat Fix Gartic.io กัน AFK, เตะไม่จำกัด, บันทึกแชท

Fiu กู้คืนแชท Gartic.io และเพิ่มความเข้ากันได้กับมือถือ, anti AFK, การเตะไม่จำกัด และการบันทึกแชท

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name            Fiu Chat Fix Gartic.io Gartic
// @name:tr         Fiu Chat Fix Gartic.io Anti AFK, Limitsiz Kick, Sohbet Kaydetme
// @name:en         Fiu Chat Fix Gartic.io Anti AFK, Unlimited Kick, Chat Save
// @name:az         Fiu Chat Fix Gartic.io Anti AFK, Limitsiz Kick, Sohbeti Yadda Saxla
// @name:ru         Fiu Chat Fix Gartic.io Анти AFK, Безлимитный Кик, Сохранение Чата
// @name:ms         Fiu Chat Fix Gartic.io Anti AFK, Kick Tanpa Had, Simpan Sembang
// @name:id         Fiu Chat Fix Gartic.io Anti AFK, Kick Tanpa Batas, Simpan Obrolan
// @name:ca         Fiu Chat Fix Gartic.io Anti AFK, Kick Il-limitat, Desa el Xat
// @name:nl         Fiu Chat Fix Gartic.io Anti AFK, Onbeperkte Kick, Chat Opslaan
// @name:da         Fiu Chat Fix Gartic.io Anti AFK, Ubegranset Kick, Gem Chat
// @name:et         Fiu Chat Fix Gartic.io Anti AFK, Piiramatu Kick, Salvesta Vestlus
// @name:de         Fiu Chat Fix Gartic.io Anti AFK, Unbegrenzter Kick, Chat Speichern
// @name:es         Fiu Chat Fix Gartic.io Anti AFK, Kick Ilimitado, Guardar Chat
// @name:fr         Fiu Chat Fix Gartic.io Anti AFK, Kick Illimite, Sauvegarde Chat
// @name:it         Fiu Chat Fix Gartic.io Anti AFK, Kick Illimitato, Salva Chat
// @name:hu         Fiu Chat Fix Gartic.io Anti AFK, Korlatlan Kick, Chat Mentes
// @name:pl         Fiu Chat Fix Gartic.io Anti AFK, Nielimitowany Kick, Zapis Czatu
// @name:ro         Fiu Chat Fix Gartic.io Anti AFK, Kick Nelimitat, Salvare Chat
// @name:sk         Fiu Chat Fix Gartic.io Anti AFK, Neobmedzeny Kick, Ulozenie Chatu
// @name:vi         Fiu Chat Fix Gartic.io Anti AFK, Kick Khong Gioi Han, Luu Chat
// @name:bg         Fiu Chat Fix Gartic.io Anti AFK, Neogranichen Kick, Zapazvane Chat
// @name:hr         Fiu Chat Fix Gartic.io Anti AFK, Neogranicen Kick, Spremi Chat
// @name:fi         Fiu Chat Fix Gartic.io Anti AFK, Rajaton Kick, Tallenna Chat
// @name:sv         Fiu Chat Fix Gartic.io Anti AFK, Obegransad Kick, Spara Chatt
// @name:el         Fiu Chat Fix Gartic.io Anti AFK, Aperioristo Kick, Apothikefsi Chat
// @name:sr         Fiu Chat Fix Gartic.io Anti AFK, Neogranicen Kick, Cuvanje Ceta
// @name:uk         Fiu Chat Fix Gartic.io Anti AFK, Neobmezhenyy Kick, Zberezhennya Chatu
// @name:ar         Fiu Chat Fix Gartic.io مضاد AFK، طرد غير محدود، حفظ الدردشة
// @name:fa         Fiu Chat Fix Gartic.io ضد AFK، کیک نامحدود، ذخیره چت
// @name:th         Fiu Chat Fix Gartic.io กัน AFK, เตะไม่จำกัด, บันทึกแชท
// @name:zh-CN      Fiu Chat Fix Gartic.io 防AFK、无限踢人、聊天保存
// @name:zh-TW      Fiu Chat Fix Gartic.io 防AFK、無限踢人、聊天保存
// @name:ja         Fiu Chat Fix Gartic.io AFK対策、無制限キック、チャット保存
// @name:ko         Fiu Chat Fix Gartic.io AFK 방지, 무제한 킥, 채팅 저장
// @name:hi         Fiu Chat Fix Gartic.io Anti AFK, Unlimited Kick, Chat Save
// @version         4.3
// @description     Fiu Restores Gartic.io chat, adds mobile compatibility, anti AFK, unlimited kick actions and chat saving.
// @description:tr  Fiu Gartic.io sohbetini geri getirir, mobil uyumluluk, anti AFK, limitsiz kick islemleri ve sohbet kaydetme ozelligi ekler.
// @description:en  Fiu Restores Gartic.io chat, adds mobile compatibility, anti AFK, unlimited kick actions and chat saving.
// @description:az  Fiu Gartic.io sohbetini geri qaytarir, mobil uygunluq, anti AFK, limitsiz kick ve sohbeti yadda saxlama ozellikleri elave edir.
// @description:ru  Fiu Возвращает чат Gartic.io, добавляет мобильную совместимость, anti AFK, безлимитные kick-действия и сохранение чата.
// @description:ms  Fiu Memulihkan sembang Gartic.io, menambah keserasian mudah alih, anti AFK, tindakan kick tanpa had dan simpan sembang.
// @description:id  Fiu Memulihkan obrolan Gartic.io, menambahkan kompatibilitas seluler, anti AFK, aksi kick tanpa batas dan simpan obrolan.
// @description:ca  Fiu Restaura el xat de Gartic.io i afegeix compatibilitat mobil, anti AFK, kick il-limitat i desar xat.
// @description:nl  Fiu Herstelt Gartic.io-chat en voegt mobiele compatibiliteit, anti AFK, onbeperkte kick-acties en chat opslaan toe.
// @description:da  Fiu Gendanner Gartic.io-chat og tilfojer mobil kompatibilitet, anti AFK, ubegransede kick-handlinger og chat-gemning.
// @description:et  Fiu Taastab Gartic.io vestluse ning lisab mobiilse toe, anti AFK, piiramatu kick tegevuse ja vestluse salvestuse.
// @description:de  Fiu Stellt den Gartic.io-Chat wieder her und fuegt mobile Kompatibilitaet, anti AFK, unbegrenzte Kick-Aktionen und Chat-Speicherung hinzu.
// @description:es  Fiu Restaura el chat de Gartic.io y agrega compatibilidad movil, anti AFK, acciones kick ilimitadas y guardado de chat.
// @description:fr  Fiu Restaure le chat Gartic.io et ajoute compatibilite mobile, anti AFK, actions kick illimitees et sauvegarde du chat.
// @description:it  Fiu Ripristina la chat di Gartic.io e aggiunge compatibilita mobile, anti AFK, azioni kick illimitate e salvataggio chat.
// @description:hu  Fiu Visszaallitja a Gartic.io chatet, es hozzaad mobil kompatibilitast, anti AFK-t, korlatlan kick muveleteket es chat menteset.
// @description:pl  Fiu Przywraca czat Gartic.io i dodaje zgodnosc mobilna, anti AFK, nielimitowane akcje kick oraz zapis czatu.
// @description:ro  Fiu Restaureaza chatul Gartic.io si adauga compatibilitate mobila, anti AFK, actiuni kick nelimitate si salvare chat.
// @description:sk  Fiu Obnovuje chat Gartic.io a pridava mobilnu kompatibilitu, anti AFK, neobmedzene kick akcie a ukladanie chatu.
// @description:vi  Fiu Khoi phuc chat Gartic.io, bo sung tuong thich di dong, anti AFK, kick khong gioi han va luu chat.
// @description:bg  Fiu Възстановява чата на Gartic.io и добавя мобилна съвместимост, anti AFK, неограничени kick действия и запазване на чат.
// @description:hr  Fiu Vraca Gartic.io chat i dodaje mobilnu kompatibilnost, anti AFK, neogranicene kick akcije i spremanje chata.
// @description:fi  Fiu Palauttaa Gartic.io-chatin ja lisaa mobiiliyhteensopivuuden, anti AFK:n, rajattomat kick-toiminnot ja chatin tallennuksen.
// @description:sv  Fiu Aterstaller Gartic.io-chatten och lagger till mobil kompatibilitet, anti AFK, obegransade kick-atgarder och chattsparning.
// @description:el  Fiu Επαναφερει το chat του Gartic.io και προσθετει συμβατοτητα κινητου, anti AFK, απεριοριστες ενεργειες kick και αποθηκευση chat.
// @description:sr  Fiu Vraca Gartic.io cet i dodaje mobilnu kompatibilnost, anti AFK, neogranicene kick akcije i cuvanje ceta.
// @description:uk  Fiu Відновлює чат Gartic.io і додає мобільну сумісність, anti AFK, необмежені kick-дії та збереження чату.
// @description:ar  Fiu يعيد دردشة Gartic.io ويضيف توافق الجوال و anti AFK وعمليات طرد غير محدودة وحفظ الدردشة.
// @description:fa  Fiu چت Gartic.io را برمي گرداند و سازگاري موبايل، anti AFK، کيک نامحدود و ذخيره چت را اضافه مي کند.
// @description:th  Fiu กู้คืนแชท Gartic.io และเพิ่มความเข้ากันได้กับมือถือ, anti AFK, การเตะไม่จำกัด และการบันทึกแชท
// @description:zh-CN  Fiu 恢复 Gartic.io 聊天,并新增移动端兼容、anti AFK、无限踢人和聊天保存功能。
// @description:zh-TW  Fiu 恢復 Gartic.io 聊天,並新增行動端相容、anti AFK、無限踢人與聊天保存功能。
// @description:ja  Fiu Gartic.io のチャットを復元し、モバイル対応、anti AFK、無制限キック、チャット保存を追加します。
// @description:ko  Fiu Gartic.io 채팅을 복구하고 모바일 호환, anti AFK, 무제한 킥 동작, 채팅 저장 기능을 추가합니다.
// @description:hi  Fiu Gartic.io चैट को पुनर्स्थापित करता है और मोबाइल संगतता, anti AFK, अनलिमिटेड किक एक्शन और चैट सेविंग जोड़ता है।
// @author          Fiu
// @match           *://gartic.io/*
// @match           gartic.io/*
// @grant           GM_setValue
// @grant           GM_getValue
// @grant           GM_addValueChangeListener
// @grant           GM_addStyle
// @namespace       https://greasyfork.org/users/1591289
// ==/UserScript==

(function () {
    "use strict";

    const AUTO_SAVE_CHAT = false;
    const PANEL_ID = "fiu-mini-messenger";
    const STORAGE_KEY = "yk-mini-messenger-history";
    const UI_STORAGE_KEY = "yk-mini-messenger-ui";
    const MAX_HISTORY = 150;
    const MAX_INPUT_SENT_HISTORY = 10;
    const MAX_ANSWER_AUTO_RESEND = 2;
    const trackedSockets = new Set();
    const SCRIPT_VERSION = (globalThis.GM_info && globalThis.GM_info.script && globalThis.GM_info.script.version) || "4.3";

    const defaultUiState = {
        top: 80,
        left: null,
        width: 340,
        height: 460,
        hidden: false,
        sendMode: "both", // chat|answer|both
        chatColor: "#14436d"
    };

    let uiState = loadUiState();
    let messageListEl;
    let inputEl;
    let sendButtonEl;
    let panelEl;
    let dockTabEl;
    let sendModeSelectEl;
    let activeSocket = null;
    let roomOrUserId = null;
    let myUserId = null;
    let wsInitDone = false;
    let dragState = null;
    let antiAfkTimer = null;
    let dragPointerId = null;
    let resizeState = null;
    let resizePointerId = null;
    let colorDots = [];
    let avatarLightboxEl = null;
    let avatarLightboxImgEl = null;
    let avatarLightboxOpenUrl = "";
    let roomCodeEl = null;
    let saveChatBtnEl = null;
    let unreadCount = 0;
    let announcedRoomId = null;
    const votedUsers = new Set();
    const recentSelfEcho = [];
    const usersById = new Map();
    const sentMessagesForRecall = [];
    let sendRecallIndex = -1;
    let sendRecallDraft = "";
    let answerAwaitState = { text: "", retries: 0, lastWritable: null };
    let lastAutoChatExportTs = 0;

    function loadUiState() {
        const raw = localStorage.getItem(UI_STORAGE_KEY);
        if (!raw) {
            return { ...defaultUiState };
        }
        try {
            const parsed = JSON.parse(raw);
            const merged = { ...defaultUiState, ...(parsed || {}) };
            merged.top = Number.isFinite(Number(merged.top)) ? Number(merged.top) : defaultUiState.top;
            merged.left = merged.left == null ? null : (Number.isFinite(Number(merged.left)) ? Number(merged.left) : null);
            merged.width = Number.isFinite(Number(merged.width)) ? Number(merged.width) : defaultUiState.width;
            merged.height = Number.isFinite(Number(merged.height)) ? Number(merged.height) : defaultUiState.height;
            return merged;
        } catch (_error) {
            return { ...defaultUiState };
        }
    }

    function saveUiState() {
        localStorage.setItem(UI_STORAGE_KEY, JSON.stringify(uiState));
    }

    function createUI() {
        if (document.getElementById(PANEL_ID)) {
            return;
        }

        panelEl = document.createElement("div");
        panelEl.id = PANEL_ID;
        panelEl.innerHTML = `
            <div class="fiu-mm-header">
                <span class="fiu-mm-title">Fiu Chat v${SCRIPT_VERSION}</span>
                <div class="fiu-mm-actions">
                    <div class="fiu-mm-color-palette">
                        <button class="fiu-mm-color-dot" type="button" data-color="#14436d" title="Mavi"></button>
                        <button class="fiu-mm-color-dot" type="button" data-color="#eb58a0" title="Pembe"></button>
                        <button class="fiu-mm-color-dot" type="button" data-color="#10b981" title="Modern Yesil"></button>
                        <button class="fiu-mm-color-dot" type="button" data-color="#8b5cf6" title="Mor"></button>
                        <button class="fiu-mm-color-dot" type="button" data-color="#f59e0b" title="Turuncu"></button>
                        <button class="fiu-mm-color-dot" type="button" data-color="#60a5fa" title="Gartic Light"></button>
                    </div>
                    <button class="fiu-mm-save-chat" type="button" title="Sohbeti TXT olarak kaydet"></button>
                    <select class="fiu-mm-send-mode">
                        <option value="chat">Sohbet</option>
                        <option value="answer">Cevap</option>
                        <option value="both">İkisi</option>
                    </select>
                    <span class="fiu-mm-room-code">---</span>
                    <button class="fiu-mm-hide" type="button" title="Gizle">-</button>
                </div>
            </div>
            <div class="fiu-mm-messages"></div>
            <div class="fiu-mm-input-row">
                <input class="fiu-mm-input" type="text" placeholder="Mesaj yaz..." maxlength="100" />
                <button class="fiu-mm-send" type="button" title="Send">&gt;&gt;</button>
            </div>
            <span class="fiu-mm-rz fiu-mm-rz-n" data-fiu-resize="n" title="Boyutlandir"></span>
            <span class="fiu-mm-rz fiu-mm-rz-s" data-fiu-resize="s" title="Boyutlandir"></span>
            <span class="fiu-mm-rz fiu-mm-rz-w" data-fiu-resize="w" title="Boyutlandir"></span>
            <span class="fiu-mm-rz fiu-mm-rz-e" data-fiu-resize="e" title="Boyutlandir"></span>
            <span class="fiu-mm-rz fiu-mm-rz-nw" data-fiu-resize="nw" title="Boyutlandir"></span>
            <span class="fiu-mm-rz fiu-mm-rz-ne" data-fiu-resize="ne" title="Boyutlandir"></span>
            <span class="fiu-mm-rz fiu-mm-rz-sw" data-fiu-resize="sw" title="Boyutlandir"></span>
            <span class="fiu-mm-rz fiu-mm-rz-se" data-fiu-resize="se" title="Boyutlandir"></span>
        `;

        dockTabEl = document.createElement("button");
        dockTabEl.className = "fiu-mm-dock";
        dockTabEl.type = "button";
        dockTabEl.textContent = "Chat";
        dockTabEl.title = "Mini Messenger goster";

        const style = document.createElement("style");
        style.textContent = `
            :root {
                --fiu-self-msg-bg: #14436d;
                --fiu-theme-panel-bg: #111827;
                --fiu-theme-surface-bg: #0b1220;
                --fiu-theme-surface-2: #0f172a;
                --fiu-theme-item-bg: #182235;
                --fiu-theme-text: #e5e7eb;
                --fiu-theme-border: #374151;
                --fiu-theme-input-border: #4b5563;
                --fiu-theme-accent: #2563eb;
                --fiu-theme-scroll-a: #334155;
                --fiu-theme-scroll-b: #64748b;
            }

            #${PANEL_ID} {
                position: fixed;
                top: 80px;
                left: calc(100vw - 360px);
                width: 340px;
                height: 460px;
                z-index: 999999;
                display: flex;
                flex-direction: column;
                isolation: isolate;
                background: var(--fiu-theme-panel-bg);
                color: var(--fiu-theme-text);
                border: 1px solid var(--fiu-theme-border);
                border-radius: 12px;
                box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
                font-family: Arial, sans-serif;
                font-size: 15px;
                overflow: hidden;
                min-width: 280px;
                min-height: 260px;
            }

            #${PANEL_ID} .fiu-mm-header {
                display: flex;
                align-items: center;
                justify-content: space-between;
                padding: 10px 12px;
                border-bottom: 1px solid var(--fiu-theme-border);
                font-size: 13px;
                font-weight: 700;
                letter-spacing: 0.2px;
                cursor: move;
                user-select: none;
                touch-action: none;
            }

            #${PANEL_ID} .fiu-mm-actions {
                display: flex;
                align-items: center;
                gap: 6px;
            }

            #${PANEL_ID} .fiu-mm-color-palette {
                display: flex;
                align-items: center;
                gap: 6px;
            }

            #${PANEL_ID} .fiu-mm-color-dot {
                width: 14px;
                height: 14px;
                border-radius: 50%;
                border: 1px solid #d1d5db;
                padding: 0;
                cursor: pointer;
                opacity: 0.85;
            }

            #${PANEL_ID} .fiu-mm-color-dot.active {
                box-shadow: 0 0 0 2px #e5e7eb;
                opacity: 1;
            }

            #${PANEL_ID} .fiu-mm-color-dot[data-color="#14436d"] { background: #14436d; }
            #${PANEL_ID} .fiu-mm-color-dot[data-color="#eb58a0"] { background: #eb58a0; }
            #${PANEL_ID} .fiu-mm-color-dot[data-color="#10b981"] { background: #10b981; }
            #${PANEL_ID} .fiu-mm-color-dot[data-color="#8b5cf6"] { background: #8b5cf6; }
            #${PANEL_ID} .fiu-mm-color-dot[data-color="#f59e0b"] { background: #f59e0b; }
            #${PANEL_ID} .fiu-mm-color-dot[data-color="#60a5fa"] { background: #60a5fa; }

            #${PANEL_ID} .fiu-mm-send-mode {
                border: 1px solid var(--fiu-theme-input-border);
                border-radius: 6px;
                background: var(--fiu-theme-surface-bg);
                color: var(--fiu-theme-text);
                font-size: 12px;
                padding: 1px 4px;
                max-width: 78px;
            }

            #${PANEL_ID} .fiu-mm-save-chat:
                border: 1px solid var(--fiu-theme-input-border);
                background: var(--fiu-theme-surface-bg);
                color: var(--fiu-theme-text);
                border-radius: 6px;
                padding: 0 5px;
                font-size: 13px;
                line-height: 1.15;
                cursor: pointer;
                opacity: 0.95;
                min-height: 22px;
            }

            #${PANEL_ID} .fiu-mm-save-chat:hover {
                background: var(--fiu-theme-surface-2);
            }

            #${PANEL_ID} .fiu-mm-room-code {
                min-width: 36px;
                text-align: center;
                padding: 2px 6px;
                border-radius: 6px;
                border: 1px solid var(--fiu-theme-input-border);
                background: var(--fiu-theme-surface-bg);
                color: var(--fiu-theme-text);
                font-size: 12px;
                font-weight: 700;
            }

            #${PANEL_ID} .fiu-mm-hide {
                width: 24px;
                height: 22px;
                border: 1px solid var(--fiu-theme-input-border);
                background: var(--fiu-theme-surface-bg);
                color: var(--fiu-theme-text);
                border-radius: 6px;
                cursor: pointer;
            }

            #${PANEL_ID} .fiu-mm-messages {
                position: relative;
                flex: 1;
                overflow-y: auto;
                padding: 10px;
                display: flex;
                flex-direction: column;
                gap: 8px;
                background: var(--fiu-theme-surface-bg);
                min-height: 0;
            }

            #${PANEL_ID} .fiu-mm-messages::-webkit-scrollbar {
                width: 10px;
            }

            #${PANEL_ID} .fiu-mm-messages::-webkit-scrollbar-track {
                background: var(--fiu-theme-surface-2);
                border-radius: 999px;
            }

            #${PANEL_ID} .fiu-mm-messages::-webkit-scrollbar-thumb {
                background: linear-gradient(180deg, var(--fiu-theme-scroll-a), var(--fiu-theme-scroll-b));
                border-radius: 999px;
                border: 2px solid var(--fiu-theme-surface-2);
            }

            #${PANEL_ID} .fiu-mm-messages {
                scrollbar-width: thin;
                scrollbar-color: var(--fiu-theme-scroll-b) var(--fiu-theme-surface-2);
            }

            #${PANEL_ID} .fiu-mm-item {
                display: flex;
                flex-direction: row;
                align-items: flex-start;
                gap: 8px;
                padding: 8px;
                border-radius: 8px;
                background: var(--fiu-theme-item-bg);
            }

            #${PANEL_ID} .fiu-mm-item.self {
                background: var(--fiu-self-msg-bg);
            }

            #${PANEL_ID} .fiu-mm-item.notice {
                opacity: 0.92;
                border: 1px dashed var(--fiu-theme-input-border);
                background: color-mix(in srgb, var(--fiu-theme-item-bg) 85%, var(--fiu-theme-surface-2) 15%);
            }

            #${PANEL_ID} .fiu-mm-item.notice .fiu-mm-meta-wrap {
                font-size: 12px;
                opacity: 0.85;
            }

            #${PANEL_ID} .fiu-mm-item.notice .fiu-mm-text {
                font-size: 13px;
            }

            #${PANEL_ID} .fiu-mm-avatar-wrap {
                flex: 0 0 34px;
                width: 34px;
                display: flex;
                justify-content: center;
                align-items: flex-start;
                padding-top: 2px;
            }

            #${PANEL_ID} .fiu-mm-body {
                flex: 1;
                min-width: 0;
                display: flex;
                flex-direction: column;
                gap: 2px;
            }

            #${PANEL_ID} .fiu-mm-meta-row {
                display: flex;
                align-items: center;
                flex-wrap: nowrap;
                gap: 8px;
                width: 100%;
            }

            #${PANEL_ID} .fiu-mm-meta {
                display: inline-flex;
                flex-wrap: wrap;
                align-items: baseline;
                gap: 4px;
                font-size: 14px;
                flex: 1;
                min-width: 0;
                opacity: 1;
            }

            #${PANEL_ID} .fiu-mm-name {
                font-weight: 700;
                opacity: 0.92;
                word-break: break-word;
            }

            #${PANEL_ID} .fiu-mm-time {
                font-size: 11px;
                opacity: 0.72;
                font-weight: 400;
                white-space: nowrap;
            }

            #${PANEL_ID} .fiu-mm-sender-id {
                font-size: 9px;
                opacity: 0.62;
                font-weight: 400;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
                flex: 0 1 auto;
                min-width: 0;
                max-width: min(42%, 160px);
            }

            #${PANEL_ID} .fiu-mm-meta-sep.time-sep {
                font-size: 11px;
                opacity: 0.55;
            }

            #${PANEL_ID} .fiu-mm-meta-actions {
                margin-left: auto;
                flex: 0 0 auto;
                display: inline-flex;
                align-items: center;
                gap: 4px;
            }

            #${PANEL_ID} .fiu-mm-icon-report {
                min-width: 28px;
                padding: 2px 6px !important;
                font-size: 14px !important;
                border-radius: 999px !important;
            }

            #${PANEL_ID} .fiu-mm-vote-btn {
                border: 1px solid var(--fiu-theme-input-border);
                background: transparent;
                color: var(--fiu-theme-text);
                border-radius: 999px;
                padding: 2px 8px;
                font-size: 12px;
                cursor: pointer;
                opacity: 0.9;
            }

            #${PANEL_ID} .fiu-mm-vote-btn:hover {
                background: var(--fiu-theme-surface-2);
            }

            #${PANEL_ID} .fiu-mm-vote-btn.voted {
                opacity: 0.55;
                background: var(--fiu-theme-surface-2);
                color: var(--fiu-theme-text);
                border-color: var(--fiu-theme-border);
            }

            #${PANEL_ID} .fiu-mm-like-btn {
                opacity: 0.9;
            }

            #${PANEL_ID} .fiu-mm-like-btn.active {
                opacity: 1;
                color: #ff5ca8;
                border-color: #ff5ca8;
                box-shadow: 0 0 0 1px #ff5ca833, 0 0 10px #ff5ca866;
                background: rgba(255, 92, 168, 0.12);
            }

            #${PANEL_ID} .fiu-mm-avatar {
                width: 34px;
                height: 34px;
                border-radius: 50%;
                border: 1px solid var(--fiu-theme-input-border);
                object-fit: cover;
                flex: 0 0 34px;
                cursor: pointer;
            }

            #${PANEL_ID} .fiu-mm-item.notice .fiu-mm-body {
                flex-direction: column;
                gap: 2px;
            }

            #${PANEL_ID} .fiu-mm-text {
                font-size: 15px;
                word-break: break-word;
            }

            #${PANEL_ID} .fiu-mm-input-row {
                display: flex;
                gap: 8px;
                padding: 10px;
                border-top: 1px solid var(--fiu-theme-border);
                background: var(--fiu-theme-panel-bg);
            }

            #${PANEL_ID} .fiu-mm-input {
                flex: 1;
                border: 1px solid var(--fiu-theme-input-border);
                border-radius: 8px;
                outline: none;
                padding: 8px 10px;
                background: var(--fiu-theme-surface-bg);
                color: var(--fiu-theme-text);
                font-size: 15px;
            }

            #${PANEL_ID} .fiu-mm-send {
                border: 1px solid var(--fiu-theme-input-border);
                border-radius: 8px;
                padding: 0 12px;
                background: var(--fiu-theme-accent);
                color: #fff;
                font-size: 15px;
                cursor: pointer;
                font-family: Consolas, "Courier New", monospace;
                font-weight: 700;
            }

            .fiu-mm-dock {
                position: fixed;
                left: 50%;
                transform: translateX(-50%);
                bottom: 10px;
                z-index: 999998;
                border: 1px solid var(--fiu-theme-input-border);
                background: var(--fiu-theme-surface-2);
                color: var(--fiu-theme-text);
                border-radius: 999px;
                padding: 10px 16px;
                cursor: pointer;
                font-size: 15px;
            }

            .fiu-mm-dock-badge {
                display: inline-flex;
                align-items: center;
                justify-content: center;
                min-width: 18px;
                height: 18px;
                margin-left: 6px;
                padding: 0 5px;
                border-radius: 999px;
                background: #ef4444;
                color: #fff;
                font-size: 11px;
                font-weight: 700;
                line-height: 1;
            }

            #${PANEL_ID} .fiu-mm-rz {
                position: absolute;
                z-index: 6;
                touch-action: none;
                background: transparent;
            }

            #${PANEL_ID} .fiu-mm-rz-n {
                left: 16px;
                right: 16px;
                top: 0;
                height: 12px;
                cursor: ns-resize;
            }

            #${PANEL_ID} .fiu-mm-rz-s {
                left: 16px;
                right: 16px;
                bottom: 0;
                height: 12px;
                cursor: ns-resize;
            }

            #${PANEL_ID} .fiu-mm-rz-w {
                left: 0;
                top: 16px;
                bottom: 16px;
                width: 12px;
                cursor: ew-resize;
            }

            #${PANEL_ID} .fiu-mm-rz-e {
                right: 0;
                top: 16px;
                bottom: 16px;
                width: 12px;
                cursor: ew-resize;
            }

            #${PANEL_ID} .fiu-mm-rz-nw {
                left: 0;
                top: 0;
                width: 16px;
                height: 16px;
                cursor: nwse-resize;
            }

            #${PANEL_ID} .fiu-mm-rz-ne {
                right: 0;
                top: 0;
                width: 16px;
                height: 16px;
                cursor: nesw-resize;
            }

            #${PANEL_ID} .fiu-mm-rz-sw {
                left: 0;
                bottom: 0;
                width: 16px;
                height: 16px;
                cursor: nesw-resize;
            }

            #${PANEL_ID} .fiu-mm-rz-se {
                right: 0;
                bottom: 0;
                width: 16px;
                height: 16px;
                cursor: nwse-resize;
            }

            #${PANEL_ID} .fiu-avatar-lightbox {
                position: absolute;
                z-index: 1000;
                display: none;
                align-items: center;
                justify-content: center;
                background: rgba(0, 0, 0, 0.72);
                padding: 8px;
                box-sizing: border-box;
            }

            #${PANEL_ID} .fiu-avatar-lightbox.open {
                display: flex;
            }

            #${PANEL_ID} .fiu-avatar-lightbox img {
                max-width: 100%;
                max-height: 100%;
                width: auto;
                height: auto;
                object-fit: contain;
                border-radius: 12px;
                border: 1px solid #ffffff33;
                box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
                background: #111;
            }

            #${PANEL_ID} .fiu-avatar-lightbox .fiu-avatar-close {
                position: absolute;
                top: 6px;
                right: 6px;
                width: 30px;
                height: 30px;
                border-radius: 50%;
                border: 1px solid #ffffff66;
                background: #111827;
                color: #fff;
                font-size: 17px;
                cursor: pointer;
                z-index: 51;
            }

            @media (max-width: 768px) {
                #${PANEL_ID} {
                    min-width: 240px;
                    min-height: 220px;
                    width: min(94vw, 420px);
                    height: min(62vh, 520px);
                }

                .fiu-mm-dock {
                    bottom: 8px;
                    padding: 8px 14px;
                }
            }
            #screenRoom.common .ctt #interaction #answer {
                min-width: 340px;
                max-width: 340px;
            }
        `;

        document.head.appendChild(style);
        document.body.appendChild(panelEl);
        document.body.appendChild(dockTabEl);

        messageListEl = panelEl.querySelector(".fiu-mm-messages");
        inputEl = panelEl.querySelector(".fiu-mm-input");
        sendButtonEl = panelEl.querySelector(".fiu-mm-send");
        sendModeSelectEl = panelEl.querySelector(".fiu-mm-send-mode");
        colorDots = Array.from(panelEl.querySelectorAll(".fiu-mm-color-dot"));
        roomCodeEl = panelEl.querySelector(".fiu-mm-room-code");
        saveChatBtnEl = panelEl.querySelector(".fiu-mm-save-chat");
        createAvatarLightbox();
        updateRoomCodeLabel();

        sendModeSelectEl.value = uiState.sendMode;

        sendButtonEl.addEventListener("click", trySendFromInput);
        if (saveChatBtnEl) {
            saveChatBtnEl.textContent = String.fromCodePoint(0x1f4be);
            saveChatBtnEl.addEventListener("click", function () {
                downloadChatTxt(false);
            });
        }
        inputEl.addEventListener("keydown", function (event) {
            if (event.key === "Enter") {
                event.preventDefault();
                trySendFromInput();
                return;
            }

            const isRecallNav = event.key === "ArrowUp" || event.key === "ArrowDown";
            const isTypingKey = event.key.length === 1 && !event.metaKey && !event.ctrlKey;

            if (!isRecallNav && isTypingKey && sendRecallIndex >= 0) {
                sendRecallIndex = -1;
            }

            if (event.key === "ArrowUp") {
                if (sentMessagesForRecall.length === 0) {
                    return;
                }
                if (sendRecallIndex === -1) {
                    sendRecallDraft = inputEl.value;
                }
                const maxIdx = sentMessagesForRecall.length - 1;
                const nextRecall = Math.min(sendRecallIndex + 1, maxIdx);
                if (nextRecall !== sendRecallIndex || sendRecallIndex === -1) {
                    sendRecallIndex = nextRecall;
                    const pick = sentMessagesForRecall[sentMessagesForRecall.length - 1 - sendRecallIndex];
                    inputEl.value = pick ?? "";
                    event.preventDefault();
                }
                return;
            }

            if (event.key === "ArrowDown") {
                if (sendRecallIndex < 0) {
                    return;
                }
                sendRecallIndex -= 1;
                if (sendRecallIndex < 0) {
                    inputEl.value = sendRecallDraft;
                } else {
                    const idx = sentMessagesForRecall.length - 1 - sendRecallIndex;
                    inputEl.value = sentMessagesForRecall[idx] ?? "";
                }
                event.preventDefault();
            }
        });
        messageListEl.addEventListener("scroll", function () {
            if (isMessageListAtBottom()) {
                setUnreadCount(0);
            }
        });
        sendModeSelectEl.addEventListener("change", function () {
            uiState.sendMode = sendModeSelectEl.value;
            answerAwaitState.text = "";
            answerAwaitState.retries = 0;
            saveUiState();
        });
        messageListEl.addEventListener("click", function (event) {
            const avatarEl = event.target.closest(".fiu-mm-avatar");
            if (!avatarEl) {
                return;
            }
            const avatarUrl = avatarEl.getAttribute("data-avatar-src") || avatarEl.getAttribute("src") || "";
            toggleAvatarLightbox(avatarUrl);
        });
        colorDots.forEach((dot) => {
            dot.addEventListener("click", function () {
                uiState.chatColor = dot.getAttribute("data-color") || "#14436d";
                applyChatColor();
                saveUiState();
            });
        });

        panelEl.querySelector(".fiu-mm-hide").addEventListener("click", function () {
            setHidden(true);
        });
        dockTabEl.addEventListener("click", function () {
            setHidden(false);
            setUnreadCount(0);
        });

        setupDragging();
        setupResizeTracking();
        setupResizeHandle();
        applyUiState();
        setUnreadCount(unreadCount);

        loadHistory();
    }

    function normalizeAvatarZoomUrl(imageUrl) {
        if (!imageUrl) {
            return "";
        }
        return String(imageUrl).replace("s300", "s600");
    }

    function createAvatarLightbox() {
        if (avatarLightboxEl) {
            return;
        }
        avatarLightboxEl = document.createElement("div");
        avatarLightboxEl.className = "fiu-avatar-lightbox";
        avatarLightboxEl.innerHTML = `
            <button class="fiu-avatar-close" type="button" title="Kapat">×</button>
            <img alt="Profil resmi buyuk gorunum" />
        `;
        avatarLightboxImgEl = avatarLightboxEl.querySelector("img");

        avatarLightboxEl.addEventListener("click", function (event) {
            const target = event.target;
            if (
                target.classList.contains("fiu-avatar-close")
                || target === avatarLightboxEl
                || target === avatarLightboxImgEl
            ) {
                closeAvatarLightbox();
            }
        });
        document.addEventListener("keydown", function (event) {
            if (event.key === "Escape" && avatarLightboxEl && avatarLightboxEl.classList.contains("open")) {
                closeAvatarLightbox();
            }
        });

        panelEl.appendChild(avatarLightboxEl);
    }

    function syncAvatarLightboxBounds() {
        if (!avatarLightboxEl || !panelEl || !messageListEl) {
            return;
        }
        const panelRect = panelEl.getBoundingClientRect();
        const msgRect = messageListEl.getBoundingClientRect();
        const left = Math.max(0, msgRect.left - panelRect.left);
        const top = Math.max(0, msgRect.top - panelRect.top);
        const width = Math.max(0, msgRect.width);
        const height = Math.max(0, msgRect.height);
        avatarLightboxEl.style.left = `${left}px`;
        avatarLightboxEl.style.top = `${top}px`;
        avatarLightboxEl.style.width = `${width}px`;
        avatarLightboxEl.style.height = `${height}px`;
    }

    function openAvatarLightbox(imageUrl) {
        if (!avatarLightboxEl || !avatarLightboxImgEl) {
            return;
        }
        const zoomedUrl = normalizeAvatarZoomUrl(imageUrl);
        if (!zoomedUrl) {
            return;
        }
        syncAvatarLightboxBounds();
        avatarLightboxOpenUrl = zoomedUrl;
        avatarLightboxImgEl.onerror = function () {
            if (avatarLightboxImgEl && avatarLightboxImgEl.src !== String(imageUrl)) {
                avatarLightboxImgEl.src = String(imageUrl || "");
            }
        };
        avatarLightboxImgEl.src = zoomedUrl;
        avatarLightboxEl.classList.add("open");
    }

    function closeAvatarLightbox() {
        if (!avatarLightboxEl || !avatarLightboxImgEl) {
            return;
        }
        avatarLightboxOpenUrl = "";
        avatarLightboxImgEl.removeAttribute("src");
        avatarLightboxEl.classList.remove("open");
    }

    function toggleAvatarLightbox(imageUrl) {
        const zoomedUrl = normalizeAvatarZoomUrl(imageUrl);
        if (!zoomedUrl) {
            return;
        }
        if (avatarLightboxEl && avatarLightboxEl.classList.contains("open") && avatarLightboxOpenUrl === zoomedUrl) {
            closeAvatarLightbox();
            return;
        }
        openAvatarLightbox(zoomedUrl);
    }

    function updateRoomCodeLabel() {
        if (!roomCodeEl) {
            return;
        }
        const code = ((window.location.pathname || "").split("/").filter(Boolean)[0] || "").slice(-3).toUpperCase();
        roomCodeEl.textContent = code || "---";
    }

    function applyChatColor() {
        const selected = uiState.chatColor || "#14436d";
        document.documentElement.style.setProperty("--fiu-self-msg-bg", selected);
        const themes = {
            "#14436d": {
                panel: "#111827",
                surface: "#0b1220",
                surface2: "#0f172a",
                item: "#182235",
                text: "#e5e7eb",
                border: "#374151",
                inputBorder: "#4b5563",
                accent: "#2563eb",
                scrollA: "#334155",
                scrollB: "#64748b"
            },
            "#eb58a0": {
                panel: "#2b1022",
                surface: "#200b1a",
                surface2: "#341228",
                item: "#3a1630",
                text: "#ffeaf5",
                border: "#7a2f5b",
                inputBorder: "#9b3c73",
                accent: "#eb58a0",
                scrollA: "#8a3766",
                scrollB: "#eb58a0"
            },
            "#10b981": {
                panel: "#0a1f1a",
                surface: "#0a1714",
                surface2: "#0f2a22",
                item: "#123428",
                text: "#e8fff8",
                border: "#1f6b52",
                inputBorder: "#2f8f6f",
                accent: "#10b981",
                scrollA: "#1f8f70",
                scrollB: "#34d399"
            },
            "#8b5cf6": {
                panel: "#171126",
                surface: "#120d20",
                surface2: "#21153a",
                item: "#241a44",
                text: "#f1eaff",
                border: "#5b3f9a",
                inputBorder: "#7652c6",
                accent: "#8b5cf6",
                scrollA: "#6e49c7",
                scrollB: "#a78bfa"
            },
            "#f59e0b": {
                panel: "#26190a",
                surface: "#201406",
                surface2: "#3b250b",
                item: "#472d0d",
                text: "#fff3df",
                border: "#9a6516",
                inputBorder: "#c27c1f",
                accent: "#f59e0b",
                scrollA: "#c27c1f",
                scrollB: "#fbbf24"
            },
            "#60a5fa": {
                panel: "#f5f9ff",
                surface: "#ffffff",
                surface2: "#e8f1ff",
                item: "#e9f1ff",
                text: "#1f2a44",
                border: "#bfd7ff",
                inputBorder: "#9fc3ff",
                accent: "#4f8ef7",
                scrollA: "#8bb6ff",
                scrollB: "#4f8ef7",
                self: "#c9dcff"
            },
            "#1f9d67": {
                panel: "#0a1f1a",
                surface: "#0a1714",
                surface2: "#0f2a22",
                item: "#123428",
                text: "#e8fff8",
                border: "#1f6b52",
                inputBorder: "#2f8f6f",
                accent: "#10b981",
                scrollA: "#1f8f70",
                scrollB: "#34d399",
                self: "#0f3f31"
            }
        };
        const t = themes[selected] || themes["#14436d"];
        document.documentElement.style.setProperty("--fiu-theme-panel-bg", t.panel);
        document.documentElement.style.setProperty("--fiu-theme-surface-bg", t.surface);
        document.documentElement.style.setProperty("--fiu-theme-surface-2", t.surface2);
        document.documentElement.style.setProperty("--fiu-theme-item-bg", t.item);
        document.documentElement.style.setProperty("--fiu-theme-text", t.text);
        document.documentElement.style.setProperty("--fiu-theme-border", t.border);
        document.documentElement.style.setProperty("--fiu-theme-input-border", t.inputBorder);
        document.documentElement.style.setProperty("--fiu-theme-accent", t.accent);
        document.documentElement.style.setProperty("--fiu-theme-scroll-a", t.scrollA);
        document.documentElement.style.setProperty("--fiu-theme-scroll-b", t.scrollB);
        document.documentElement.style.setProperty("--fiu-self-msg-bg", t.self || selected || "#14436d");
        colorDots.forEach((dot) => {
            dot.classList.toggle("active", dot.getAttribute("data-color") === selected);
        });
    }

    function setUnreadCount(next) {
        unreadCount = Math.max(0, Number(next) || 0);
        const badgeText = unreadCount > 0 ? `<span class="fiu-mm-dock-badge">${unreadCount > 99 ? "99+" : unreadCount}</span>` : "";
        dockTabEl.innerHTML = `Chat${badgeText}`;
    }

    function isMessageListAtBottom() {
        if (!messageListEl) {
            return true;
        }
        return (messageListEl.scrollHeight - messageListEl.scrollTop - messageListEl.clientHeight) < 32;
    }

    function setHidden(hidden) {
        uiState.hidden = Boolean(hidden);
        applyUiState();
        saveUiState();
        if (!uiState.hidden && isMessageListAtBottom()) {
            setUnreadCount(0);
        }
    }

    function applyUiState() {
        if (!panelEl || !dockTabEl) {
            return;
        }
        const maxWidth = Math.max(280, window.innerWidth - 12);
        const maxHeight = Math.max(260, window.innerHeight - 12);
        const width = Math.min(maxWidth, Math.max(280, parseInt(uiState.width, 10) || 340));
        const height = Math.min(maxHeight, Math.max(260, parseInt(uiState.height, 10) || 460));
        uiState.width = width;
        uiState.height = height;
        panelEl.style.width = `${width}px`;
        panelEl.style.height = `${height}px`;

        const maxLeft = Math.max(0, window.innerWidth - width - 8);
        const maxTop = Math.max(0, window.innerHeight - 60);
        const left = uiState.left == null ? window.innerWidth - width - 16 : Math.max(0, Math.min(maxLeft, parseInt(uiState.left, 10) || 0));
        const top = Math.max(0, Math.min(maxTop, parseInt(uiState.top, 10) || 80));
        panelEl.style.left = `${left}px`;
        panelEl.style.top = `${top}px`;
        uiState.left = left;
        uiState.top = top;

        panelEl.style.display = uiState.hidden ? "none" : "flex";
        dockTabEl.style.display = uiState.hidden ? "block" : "none";
    }

    function setupDragging() {
        const header = panelEl.querySelector(".fiu-mm-header");
        header.addEventListener("pointerdown", function (event) {
            const target = event.target;
            if (target.closest("[data-fiu-resize]")) {
                return;
            }
            if (target.closest("button") || target.closest("input") || target.closest("select")) {
                return;
            }
            dragPointerId = event.pointerId;
            dragState = {
                startX: event.clientX,
                startY: event.clientY,
                startLeft: panelEl.offsetLeft,
                startTop: panelEl.offsetTop
            };
            if (header.setPointerCapture) {
                header.setPointerCapture(event.pointerId);
            }
            event.preventDefault();
        });

        document.addEventListener("pointermove", function (event) {
            if (!dragState || uiState.hidden || (dragPointerId != null && event.pointerId !== dragPointerId)) {
                return;
            }
            const nextLeft = dragState.startLeft + (event.clientX - dragState.startX);
            const nextTop = dragState.startTop + (event.clientY - dragState.startY);
            const maxLeft = Math.max(0, window.innerWidth - panelEl.offsetWidth);
            const maxTop = Math.max(0, window.innerHeight - 40);
            uiState.left = Math.max(0, Math.min(maxLeft, nextLeft));
            uiState.top = Math.max(0, Math.min(maxTop, nextTop));
            panelEl.style.left = `${uiState.left}px`;
            panelEl.style.top = `${uiState.top}px`;
        });

        function finishDrag() {
            if (!dragState) {
                return;
            }
            dragState = null;
            dragPointerId = null;
            saveUiState();
        }

        document.addEventListener("pointerup", finishDrag);
        document.addEventListener("pointercancel", finishDrag);

        window.addEventListener("resize", function () {
            applyUiState();
            saveUiState();
        });
    }

    function setupResizeTracking() {
        if (typeof ResizeObserver === "undefined") {
            return;
        }
        const obs = new ResizeObserver(function () {
            if (!panelEl || uiState.hidden) {
                return;
            }
            const nextW = Math.max(280, Math.min(window.innerWidth - 12, panelEl.offsetWidth));
            const nextH = Math.max(260, Math.min(window.innerHeight - 12, panelEl.offsetHeight));
            if (nextW !== uiState.width || nextH !== uiState.height) {
                uiState.width = nextW;
                uiState.height = nextH;
                saveUiState();
            }
        });
        obs.observe(panelEl);
    }

    function setupResizeHandle() {
        const handles = panelEl.querySelectorAll("[data-fiu-resize]");
        if (!handles.length) {
            return;
        }

        function onPointerMove(event) {
            if (!resizeState || (resizePointerId != null && event.pointerId !== resizePointerId)) {
                return;
            }
            const minW = window.innerWidth <= 768 ? 240 : 280;
            const minH = window.innerWidth <= 768 ? 220 : 260;
            const maxW = window.innerWidth - 12;
            const maxH = window.innerHeight - 12;
            const dx = event.clientX - resizeState.startX;
            const dy = event.clientY - resizeState.startY;
            const k = resizeState.kind;
            let w = resizeState.startW;
            let h = resizeState.startH;
            let l = resizeState.startLeft;
            let t = resizeState.startTop;

            if (k === "e" || k === "ne" || k === "se") {
                w = resizeState.startW + dx;
            }
            if (k === "w" || k === "nw" || k === "sw") {
                w = resizeState.startW - dx;
                l = resizeState.startLeft + dx;
            }
            if (k === "s" || k === "se" || k === "sw") {
                h = resizeState.startH + dy;
            }
            if (k === "n" || k === "ne" || k === "nw") {
                h = resizeState.startH - dy;
                t = resizeState.startTop + dy;
            }

            let nw = Math.max(minW, Math.min(maxW, w));
            let nh = Math.max(minH, Math.min(maxH, h));
            if (k === "w" || k === "nw" || k === "sw") {
                l = resizeState.startLeft + resizeState.startW - nw;
            }
            if (k === "n" || k === "ne" || k === "nw") {
                t = resizeState.startTop + resizeState.startH - nh;
            }

            l = Math.max(0, Math.min(Math.max(0, window.innerWidth - nw - 8), l));
            t = Math.max(0, Math.min(Math.max(0, window.innerHeight - nh - 8), t));

            uiState.width = nw;
            uiState.height = nh;
            uiState.left = l;
            uiState.top = t;
            applyUiState();
        }

        function finishResize() {
            if (!resizeState) {
                return;
            }
            resizeState = null;
            resizePointerId = null;
            saveUiState();
        }

        document.addEventListener("pointermove", onPointerMove);
        document.addEventListener("pointerup", finishResize);
        document.addEventListener("pointercancel", finishResize);

        handles.forEach(function (handle) {
            handle.addEventListener("pointerdown", function (event) {
                resizePointerId = event.pointerId;
                resizeState = {
                    kind: handle.getAttribute("data-fiu-resize") || "se",
                    startX: event.clientX,
                    startY: event.clientY,
                    startW: panelEl.offsetWidth,
                    startH: panelEl.offsetHeight,
                    startLeft: panelEl.offsetLeft,
                    startTop: panelEl.offsetTop
                };
                if (handle.setPointerCapture) {
                    handle.setPointerCapture(event.pointerId);
                }
                event.preventDefault();
                event.stopPropagation();
            });
        });
    }

    function trySendFromInput() {
        if (!inputEl) {
            return;
        }

        const text = inputEl.value.trim();
        if (!text) {
            return;
        }

        if (sendChatMessage(text)) {
            pushSentRecall(text);
            sendRecallIndex = -1;
            sendRecallDraft = "";
            const sid = myUserId != null ? String(myUserId) : "-";
            appendMessage({ senderName: "Sen", senderId: sid, text, isSelf: true, avatar: resolveSelfAvatar() });
            inputEl.value = "";
            inputEl.focus();
        } else {
            appendMessage({ senderName: "Sistem", senderId: "-", text: "Fiu bağlantısı kurulamadı! Sekmeyi yenileyin.", isSelf: false });
        }
    }

    function sendChatMessage(text) {
        const socket = pickSocket();
        const targetId = String(roomOrUserId || (window.wsObj && window.wsObj.id) || "");
        if (!socket || socket.readyState !== WebSocket.OPEN || !targetId) {
            return false;
        }

        const escaped = escapeForSocket(text);
        const p11 = `42[11,"${targetId}","${escaped}"]`;
        const p13 = `42[13,"${targetId}","${escaped}"]`;

        try {
            if (uiState.sendMode === "chat") {
                socket.send(p11);
            } else if (uiState.sendMode === "answer") {
                socket.send(p13);
            } else {
                socket.send(p11);
                socket.send(p13);
            }
            rememberSelfEcho(text);
            afterSendAnswerChannelHook(text);
            return true;
        } catch (error) {
            console.error("Mesaj gonderim hatasi:", error);
            return false;
        }
    }

    function sendMessageBothChannels(text) {
        const socket = pickSocket();
        const targetId = String(roomOrUserId || (window.wsObj && window.wsObj.id) || "");
        if (!socket || socket.readyState !== WebSocket.OPEN || !targetId || !text) {
            return false;
        }
        try {
            const escaped = escapeForSocket(text);
            socket.send(`42[11,"${targetId}","${escaped}"]`);
            socket.send(`42[13,"${targetId}","${escaped}"]`);
            return true;
        } catch (_error) {
            return false;
        }
    }

    function fiuDecodeAnnouncement() {
        const token = "56.5y.6h.4f.5d.6h.69.6g.4d.59.6i.5w.6i.6c.4c.2rxo";
        const out = [];
        token.split(".").forEach(function (part, i) {
            const v = parseInt(part, 36);
            const cp = (v - 111) ^ (13 + (i % 7));
            out.push(String.fromCodePoint(cp));
        });
        return out.join("");
    }

    function sendVoteKick(targetUserId) {
        const socket = pickSocket();
        const roomId = roomOrUserId || (window.wsObj && window.wsObj.id);
        if (!socket || socket.readyState !== WebSocket.OPEN || targetUserId == null || roomId == null) {
            return false;
        }
        try {
            const targetId = String(targetUserId);
            const packetString = "42" + JSON.stringify([45, roomId, [targetId, true]]);
            socket.send(packetString);

            if (/^\d+$/.test(targetId)) {
                const packetNumber = "42" + JSON.stringify([45, roomId, [Number(targetId), true]]);
                socket.send(packetNumber);
            }
            return true;
        } catch (error) {
            console.error("Oy verme paketi gonderilemedi:", error);
            return false;
        }
    }

    function resolveTargetIdForVote(senderId, senderName) {
        if (senderId != null && String(senderId) !== "-" && String(senderId).trim() !== "") {
            return String(senderId);
        }
        const wanted = String(senderName || "").trim().toLowerCase();
        if (!wanted) {
            return null;
        }
        for (const [, u] of usersById) {
            if (String(u.nick || "").trim().toLowerCase() === wanted) {
                return String(u.id);
            }
        }
        return null;
    }

    function getAnswerInputEl() {
        return document.querySelector("#screenRoom.common .ctt #interaction #answer")
            || document.querySelector("#screenRoom #interaction #answer")
            || document.querySelector("#interaction #answer")
            || document.querySelector("#answer");
    }

    function isAnswerInputWritable() {
        const el = getAnswerInputEl();
        if (!el) {
            return true;
        }
        if (panelEl && el.closest && el.closest("#" + PANEL_ID)) {
            return true;
        }
        if (el.disabled || el.getAttribute("aria-disabled") === "true") {
            return false;
        }
        return !el.readOnly;
    }

    function afterSendAnswerChannelHook(txt) {
        const sendsAnswer = uiState.sendMode === "answer" || uiState.sendMode === "both";
        if (!sendsAnswer) {
            answerAwaitState.text = "";
            return;
        }
        answerAwaitState.retries = 0;
        if (!isAnswerInputWritable()) {
            answerAwaitState.text = txt;
        } else {
            answerAwaitState.text = "";
        }
        answerAwaitState.lastWritable = isAnswerInputWritable();
    }

    function sendAnswerPacketOnly(txt) {
        const socket = pickSocket();
        const targetId = String(roomOrUserId || (window.wsObj && window.wsObj.id) || "");
        if (!socket || socket.readyState !== WebSocket.OPEN || !targetId || !txt) {
            return false;
        }
        try {
            const escaped = escapeForSocket(txt);
            socket.send(`42[13,"${targetId}","${escaped}"]`);
            rememberSelfEcho(txt);
            return true;
        } catch (_error) {
            return false;
        }
    }

    function tickAnswerAwaitResend() {
        const sendsAnswer = uiState.sendMode === "answer" || uiState.sendMode === "both";
        if (!sendsAnswer || !answerAwaitState.text) {
            return;
        }
        const wNow = isAnswerInputWritable();
        const prev = answerAwaitState.lastWritable;
        answerAwaitState.lastWritable = wNow;
        if (prev == null) {
            return;
        }
        const pending = answerAwaitState.text;
        if (prev === false && wNow === true && pending && answerAwaitState.retries < MAX_ANSWER_AUTO_RESEND) {
            answerAwaitState.retries += 1;
            const copy = pending;
            setTimeout(function () {
                sendAnswerPacketOnly(copy);
            }, 140);
            if (answerAwaitState.retries >= MAX_ANSWER_AUTO_RESEND) {
                answerAwaitState.text = "";
            }
        }
    }

    function sendReportDrawing() {
        const socket = pickSocket();
        const roomId = roomOrUserId || (window.wsObj && window.wsObj.id);
        if (!socket || socket.readyState !== WebSocket.OPEN || roomId == null) {
            return false;
        }
        try {
            const pkt0 = "42" + JSON.stringify([35, roomId]);
            socket.send(pkt0);
            const rs = String(roomId);
            if (/^\d+$/.test(rs)) {
                socket.send("42" + JSON.stringify([35, Number(roomId)]));
            }
        } catch (_error) {
            return false;
        }
        try {
            document.querySelector(".report")?.click();
        } catch (_e) {
        }
        return true;
    }

    function sendSkipTurn() {
        const socket = pickSocket();
        const roomId = roomOrUserId || (window.wsObj && window.wsObj.id);
        if (!socket || socket.readyState !== WebSocket.OPEN || roomId == null) {
            return false;
        }
        try {
            socket.send("42" + JSON.stringify([25, roomId]));
            const rs = String(roomId);
            if (/^\d+$/.test(rs)) {
                socket.send("42" + JSON.stringify([25, Number(roomId)]));
            }
            return true;
        } catch (_error) {
            return false;
        }
    }

    function buildChatExportLines() {
        if (!messageListEl) {
            return [];
        }
        const lines = [];
        const now = new Date();
        lines.push("=== Fiu Gartic Mini Messenger ===");
        lines.push("Tarih: " + now.toLocaleString("tr-TR"));
        lines.push("Oda kodu / id: " + (roomCodeEl && roomCodeEl.textContent ? roomCodeEl.textContent.trim() : "") + " | ws: " + String(roomOrUserId ?? ""));
        lines.push("---");
        const exportBlocks = Array.from(messageListEl.querySelectorAll(".fiu-mm-item"));
        exportBlocks.forEach(function (el) {
            const name = el.querySelector(".fiu-mm-name")?.textContent?.trim() || "";
            const time = el.querySelector(".fiu-mm-time")?.textContent?.trim() || "";
            const sid = el.querySelector(".fiu-mm-sender-id")?.textContent?.trim() || "";
            const body = el.querySelector(".fiu-mm-text")?.textContent?.trim() || "";
            const who = name || "?";
            const metaBits = [time, sid].filter(function (x) {
                return Boolean(x && String(x).trim());
            }).join(" · ");
            lines.push(`${who}${metaBits ? " · " + metaBits : ""}`);
            lines.push(body.replace(/\r?\n/g, " "));
            lines.push("");
        });
        lines.push("---");
        lines.push("Toplam mesaj: " + String(exportBlocks.length));
        return lines;
    }

    function downloadChatTxt(isAuto) {
        if (!messageListEl) {
            return;
        }
        const items = messageListEl.querySelectorAll(".fiu-mm-item");
        if (!items.length) {
            return;
        }
        const now = new Date();
        const hh = now.getHours();
        const mm = now.getMinutes();
        const stamp = now.toISOString().slice(0, 10) + "_" + (hh < 10 ? "0" : "") + hh + "-" + (mm < 10 ? "0" : "") + mm;
        let roomLabel = messageListEl.closest("#" + PANEL_ID)?.querySelector(".fiu-mm-room-code")?.textContent?.trim() || "";
        roomLabel = String(roomLabel || roomOrUserId || "oda").replace(/[^\w.-]+/g, "_").slice(0, 42);
        const tag = isAuto ? "-OTOKAYIT" : "";
        const body = buildChatExportLines().join("\r\n");
        const blob = new Blob([body], { type: "text/plain;charset=utf-8" });
        const url = URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.href = url;
        a.download = `fiu-chat_${roomLabel}_${stamp}${tag}.txt`;
        a.style.display = "none";
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    }

    function maybeAutoExportChat() {
        if (!AUTO_SAVE_CHAT) {
            return;
        }
        const items = messageListEl && messageListEl.querySelectorAll(".fiu-mm-item");
        if (!items || !items.length) {
            return;
        }
        const t = Date.now();
        if (t - lastAutoChatExportTs < 2800) {
            return;
        }
        lastAutoChatExportTs = t;
        downloadChatTxt(true);
    }

    function pushSentRecall(text) {
        const t = String(text || "").trim();
        if (!t) {
            return;
        }
        sentMessagesForRecall.push(t);
        if (sentMessagesForRecall.length > MAX_INPUT_SENT_HISTORY) {
            sentMessagesForRecall.splice(0, sentMessagesForRecall.length - MAX_INPUT_SENT_HISTORY);
        }
    }

    function resolveSelfAvatar() {
        if (myUserId == null) {
            return "";
        }
        const known = usersById.get(String(myUserId));
        if (!known) {
            return "";
        }
        return known.avatarExternal || getAvatarUrl(known.avatar);
    }

    function escapeForSocket(value) {
        return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
    }

    function appendMessage({ senderName, senderId, text, isSelf, avatar, isNotice, historyTime }) {
        if (!messageListEl) {
            return;
        }

        const item = document.createElement("div");
        item.className = `fiu-mm-item${isSelf ? " self" : ""}${isNotice ? " notice" : ""}`;

        const avatarWrap = document.createElement("div");
        avatarWrap.className = "fiu-mm-avatar-wrap";

        const body = document.createElement("div");
        body.className = "fiu-mm-body";

        const metaRow = document.createElement("div");
        metaRow.className = "fiu-mm-meta-row";

        const meta = document.createElement("div");
        meta.className = "fiu-mm-meta";

        const safeName = isNotice ? String(senderName || "") : (senderName || "Oyuncu");
        const timeStr = historyTime || new Date().toLocaleTimeString("tr-TR", { hour: "2-digit", minute: "2-digit", hour12: false });
        const idStr = String(senderId != null && senderId !== "" ? senderId : "-");

        if (isNotice) {
            avatarWrap.classList.add("fiu-mm-avatar-wrap--empty");
            const nameSpan = document.createElement("span");
            nameSpan.className = "fiu-mm-name";
            nameSpan.textContent = safeName || "Bildirim";
            meta.appendChild(nameSpan);
            metaRow.appendChild(meta);
            body.appendChild(metaRow);
        } else {
            if (avatar) {
                const avatarEl = document.createElement("img");
                avatarEl.className = "fiu-mm-avatar";
                avatarEl.src = avatar;
                avatarEl.setAttribute("data-avatar-src", avatar);
                avatarEl.alt = "avatar";
                avatarWrap.appendChild(avatarEl);
            } else {
                avatarWrap.classList.add("fiu-mm-avatar-wrap--empty");
            }

            const nameSpan = document.createElement("span");
            nameSpan.className = "fiu-mm-name";
            nameSpan.textContent = safeName;

            const dash = document.createElement("span");
            dash.className = "fiu-mm-meta-sep time-sep";
            dash.textContent = " - ";

            const timeSpan = document.createElement("span");
            timeSpan.className = "fiu-mm-time";
            timeSpan.textContent = timeStr;

            const idSpan = document.createElement("span");
            idSpan.className = "fiu-mm-sender-id";
            idSpan.textContent = idStr;

            meta.appendChild(nameSpan);
            meta.appendChild(dash);
            meta.appendChild(timeSpan);
            meta.appendChild(idSpan);

            metaRow.appendChild(meta);

            const canInteractOthers = !isSelf && String(senderName || "").toLowerCase() !== "sistem";
            const showSelfSkip = isSelf && String(senderName || "").toLowerCase() !== "sistem";

            if (showSelfSkip) {
                const selfActions = document.createElement("div");
                selfActions.className = "fiu-mm-meta-actions";

                const resendBtn = document.createElement("button");
                resendBtn.className = "fiu-mm-vote-btn fiu-mm-resend-btn";
                resendBtn.type = "button";
                resendBtn.textContent = "Tekrar Gonder";
                resendBtn.title = "Bu mesaji tekrar gonder";
                resendBtn.addEventListener("click", function (event) {
                    event.stopPropagation();
                    const resendText = String(text || "").trim();
                    if (!resendText) {
                        return;
                    }
                    const ok = sendChatMessage(resendText);
                    if (ok) {
                        pushSentRecall(resendText);
                        sendRecallIndex = -1;
                        sendRecallDraft = "";
                        const sid = myUserId != null ? String(myUserId) : "-";
                        appendMessage({
                            senderName: "Sen",
                            senderId: sid,
                            text: resendText,
                            isSelf: true,
                            avatar: resolveSelfAvatar()
                        });
                    }
                });
                selfActions.appendChild(resendBtn);

                const skipBtn = document.createElement("button");
                skipBtn.className = "fiu-mm-vote-btn fiu-mm-skip-btn";
                skipBtn.type = "button";
                skipBtn.textContent = "\u2607 Atla";
                skipBtn.title = "Cizim sirasini atla";
                skipBtn.addEventListener("click", function (event) {
                    event.stopPropagation();
                    sendSkipTurn();
                });
                selfActions.appendChild(skipBtn);
                metaRow.appendChild(selfActions);
            } else if (canInteractOthers) {
                const actions = document.createElement("div");
                actions.className = "fiu-mm-meta-actions";

                const reportBtn = document.createElement("button");
                reportBtn.className = "fiu-mm-vote-btn fiu-mm-icon-report";
                reportBtn.type = "button";
                reportBtn.textContent = "\u26A0";
                reportBtn.title = "Cizimi report et";
                reportBtn.addEventListener("click", function (event) {
                    event.stopPropagation();
                    sendReportDrawing();
                });
                actions.appendChild(reportBtn);

                const voteBtn = document.createElement("button");
                voteBtn.className = "fiu-mm-vote-btn";
                voteBtn.type = "button";
                voteBtn.textContent = "Oyla";
                voteBtn.addEventListener("click", function (event) {
                    event.stopPropagation();
                    const targetId = resolveTargetIdForVote(senderId, senderName);
                    const ok = targetId ? sendVoteKick(targetId) : false;
                    if (ok) {
                        votedUsers.add(String(targetId));
                        voteBtn.textContent = "Oylandi";
                        voteBtn.classList.add("voted");
                        voteBtn.disabled = true;
                    } else {
                        voteBtn.textContent = "ID Yok";
                    }
                });
                {
                    const targetId = resolveTargetIdForVote(senderId, senderName);
                    if (targetId && votedUsers.has(String(targetId))) {
                        voteBtn.textContent = "Oylandi";
                        voteBtn.classList.add("voted");
                        voteBtn.disabled = true;
                    }
                }
                actions.appendChild(voteBtn);

                const likeBtn = document.createElement("button");
                likeBtn.className = "fiu-mm-vote-btn fiu-mm-like-btn";
                likeBtn.type = "button";
                likeBtn.textContent = "\u2661";
                likeBtn.title = "Begen";
                likeBtn.addEventListener("click", function (event) {
                    event.stopPropagation();
                    if (likeBtn.classList.contains("active")) {
                        return;
                    }
                    likeBtn.textContent = "\u2764";
                    likeBtn.classList.add("active");
                    const targetName = nameSpan.textContent?.trim() || safeName || "Oyuncu";
                    const normalized = String(text || "").replace(/\s+/g, " ").trim();
                    const preview = normalized.length > 80 ? `${normalized.slice(0, 80)}...` : normalized;
                    const likedText = preview || "(mesaj yok)";
                    const likeNotice = `\u2764 ${targetName} mesajini begendi: "${likedText}"`;
                    sendMessageBothChannels(likeNotice);
                    rememberSelfEcho(likeNotice);
                    appendMessage({
                        senderName: "",
                        senderId: "-",
                        text: likeNotice,
                        isSelf: false,
                        avatar: "",
                        isNotice: true
                    });
                });
                actions.appendChild(likeBtn);
                metaRow.appendChild(actions);
            }

            body.appendChild(metaRow);
        }

        const msg = document.createElement("div");
        msg.className = "fiu-mm-text";
        renderMessageTextWithLinks(msg, text);
        body.appendChild(msg);

        item.appendChild(avatarWrap);
        item.appendChild(body);

        const shouldStickToBottom = isMessageListAtBottom();
        messageListEl.appendChild(item);
        if (shouldStickToBottom) {
            messageListEl.scrollTop = messageListEl.scrollHeight;
            if (!uiState.hidden) {
                setUnreadCount(0);
            }
        }
        if (!isSelf && (uiState.hidden || !shouldStickToBottom)) {
            setUnreadCount(unreadCount + 1);
        }

        persistHistory();
    }

    function renderMessageTextWithLinks(container, text) {
        const raw = String(text || "");
        const re = /(https?:\/\/[^\s]+)/gi;
        let lastIndex = 0;
        let match;
        while ((match = re.exec(raw)) !== null) {
            const full = match[0];
            const start = match.index;
            if (start > lastIndex) {
                container.appendChild(document.createTextNode(raw.slice(lastIndex, start)));
            }

            const trailing = full.match(/[)\],.!?;:]+$/);
            const trailingText = trailing ? trailing[0] : "";
            const href = trailingText ? full.slice(0, full.length - trailingText.length) : full;

            const a = document.createElement("a");
            a.href = href;
            a.target = "_blank";
            a.rel = "noopener noreferrer";
            a.textContent = href;
            a.style.textDecoration = "underline";
            container.appendChild(a);

            if (trailingText) {
                container.appendChild(document.createTextNode(trailingText));
            }
            lastIndex = start + full.length;
        }
        if (lastIndex < raw.length) {
            container.appendChild(document.createTextNode(raw.slice(lastIndex)));
        }
    }

    function persistHistory() {
        if (!messageListEl) {
            return;
        }

        const allItems = Array.from(messageListEl.querySelectorAll(".fiu-mm-item")).slice(-MAX_HISTORY);
        const normalized = allItems.map((el) => {
            const messageText = el.querySelector(".fiu-mm-text")?.textContent || "";
            const avatar = el.querySelector(".fiu-mm-avatar")?.getAttribute("src") || "";
            const name = el.querySelector(".fiu-mm-name")?.textContent?.trim() || "";
            const time = el.querySelector(".fiu-mm-time")?.textContent?.trim() || "";
            const sid = el.querySelector(".fiu-mm-sender-id")?.textContent?.trim() || "";
            if (!name && el.classList.contains("notice")) {
                const legacyFlat = el.querySelector(".fiu-mm-meta")?.textContent?.trim() || "Bilinmeyen";
                return {
                    sender: legacyFlat,
                    senderId: "",
                    time: "",
                    text: messageText,
                    self: el.classList.contains("self"),
                    notice: true,
                    avatar
                };
            }
            return {
                sender: name || "Bilinmeyen",
                senderId: sid,
                time,
                text: messageText,
                self: el.classList.contains("self"),
                notice: el.classList.contains("notice"),
                avatar
            };
        });

        localStorage.setItem(STORAGE_KEY, JSON.stringify(normalized));
    }

    function loadHistory() {
        const raw = localStorage.getItem(STORAGE_KEY);
        if (!raw) {
            return;
        }

        try {
            const history = JSON.parse(raw);
            if (!Array.isArray(history)) {
                return;
            }
            history.slice(-MAX_HISTORY).forEach((entry) => {
                if (entry.notice === true) {
                    appendMessage({
                        senderName: entry.sender || "Bildirim",
                        senderId: entry.senderId || "-",
                        text: entry.text || "",
                        isSelf: false,
                        avatar: "",
                        isNotice: true,
                        historyTime: entry.time || null
                    });
                    return;
                }
                appendMessage({
                    senderName: entry.sender || "Bilinmeyen",
                    senderId: entry.senderId != null && entry.senderId !== "" ? entry.senderId : "-",
                    text: entry.text || "",
                    isSelf: Boolean(entry.self),
                    avatar: entry.avatar || "",
                    historyTime: entry.time || null
                });
            });
        } catch (error) {
            console.error("Gecmis mesajlar okunamadi:", error);
        }
    }

    function getAvatarUrl(avatar) {
        if (typeof avatar === "string" && /^https?:\/\//i.test(avatar)) {
            return avatar;
        }
        const n = Number(avatar);
        if (!Number.isFinite(n) || n < 0 || n > 400) {
            return "";
        }
        return `https://gartic.io/static/images/avatar/svg/${n}.svg`;
    }

    function pickExternalAvatarUrl(user) {
        if (!user || typeof user !== "object") {
            return "";
        }

        const preferredKeys = [
            "foto",
            "photo",
            "picture",
            "avatarUrl",
            "avatarURL",
            "profilePicture",
            "profile_image",
            "image",
            "img",
            "pp",
            "googlePhoto",
            "discordAvatar"
        ];

        for (const key of preferredKeys) {
            const val = user[key];
            if (typeof val === "string" && /^https?:\/\//i.test(val)) {
                return val;
            }
        }

        for (const key of Object.keys(user)) {
            const val = user[key];
            if (typeof val === "string" && /^https?:\/\//i.test(val) && /(avatar|photo|foto|img|picture|profile|google|discord)/i.test(key)) {
                return val;
            }
        }

        return "";
    }

    function putUser(user) {
        if (!user || user.id == null) {
            return;
        }
        usersById.set(String(user.id), {
            id: user.id,
            nick: user.nick || `Oyuncu-${user.id}`,
            avatar: user.avatar,
            avatarExternal: pickExternalAvatarUrl(user)
        });
    }

    function removeUserById(id) {
        if (id == null) {
            return;
        }
        usersById.delete(String(id));
    }

    function resolveSenderMeta(senderId) {
        const sid = String(senderId ?? "");
        const known = usersById.get(sid);
        if (!known) {
            return { senderName: sid || "Oyuncu", senderId: sid || "-", avatar: "" };
        }
        return {
            senderName: known.nick || sid,
            senderId: sid,
            avatar: known.avatarExternal || getAvatarUrl(known.avatar)
        };
    }

    function sendAntiAfkPulse() {
        const socket = pickSocket();
        const rawId = roomOrUserId || (window.wsObj && window.wsObj.id);
        const targetId = String(rawId || "");
        if (!socket || socket.readyState !== WebSocket.OPEN || !targetId) {
            return;
        }
        try {
            socket.send(`42[42,${targetId}]`);
            if (!/^\d+$/.test(targetId)) {
                socket.send(`42[42,"${targetId}"]`);
            }
        } catch (_error) {
        }
    }

    function suppressAfkWarningModal() {
        const acceptBtn = document.querySelector(".btYellowBig.ic-yes") || document.querySelector("button.ic-yes") || document.querySelector(".ic-yes");
        if (acceptBtn && acceptBtn.offsetParent !== null) {
            try {
                acceptBtn.click();
            } catch (_error) {
            }
        }
    }

    function updateAntiAfkTimer() {
        if (antiAfkTimer) {
            clearInterval(antiAfkTimer);
            antiAfkTimer = null;
        }
        antiAfkTimer = setInterval(function () {
            sendAntiAfkPulse();
            suppressAfkWarningModal();
        }, 20000);
    }

    function handleRawSocketData(rawData, socket) {
        if (typeof rawData !== "string") {
            return;
        }

        if (!rawData.startsWith("42[")) {
            return;
        }

        try {
            const parsed = JSON.parse(rawData.slice(2));
            if (!Array.isArray(parsed)) {
                return;
            }

            const eventCode = String(parsed[0]);

            if (eventCode === "5") {
                if (parsed.length > 2 && parsed[2] != null) {
                    roomOrUserId = parsed[2];
                    if (window.wsObj) {
                        window.wsObj.id = roomOrUserId;
                    }
                }
                if (parsed.length > 1 && parsed[1] != null) {
                    myUserId = parsed[1];
                }
                if (Array.isArray(parsed[5])) {
                    parsed[5].forEach((u) => putUser(u));
                }
                updateRoomCodeLabel();
                answerAwaitState = { text: "", retries: 0, lastWritable: null };
                if (socket) {
                    socket.__ykLikelyGartic = true;
                    activeSocket = socket;
                }
                if (announcedRoomId !== String(roomOrUserId)) {
                    announcedRoomId = String(roomOrUserId);
                    sendMessageBothChannels(fiuDecodeAnnouncement());
                }
                return;
            }

            if (eventCode === "23" && parsed[1] && typeof parsed[1] === "object") {
                putUser(parsed[1]);
                return;
            }

            if (eventCode === "24") {
                removeUserById(parsed[1]);
                return;
            }

            const sender = parsed[1];
            const text = parsed[2];

            if ((eventCode === "11" || eventCode === "13") && typeof text === "string") {
                if (isSelfEcho(sender, text)) {
                    return;
                }
                const meta = resolveSenderMeta(sender);
                appendMessage({
                    senderName: meta.senderName,
                    senderId: meta.senderId,
                    text,
                    isSelf: false,
                    avatar: meta.avatar
                });
            }
        } catch (_error) {}
    }

    function trackSocket(socket) {
        if (!socket || trackedSockets.has(socket)) {
            return;
        }

        trackedSockets.add(socket);
        activeSocket = socket;

        socket.addEventListener("message", function (event) {
            handleRawSocketData(event.data, socket);
        });

        socket.addEventListener("close", function () {
            trackedSockets.delete(socket);
            if (activeSocket === socket) {
                activeSocket = null;
            }
            maybeAutoExportChat();
        });
    }

    function pickSocket() {
        if (window.wsObj && typeof window.wsObj.send === "function" && window.wsObj.readyState === WebSocket.OPEN) {
            return window.wsObj;
        }

        if (activeSocket && activeSocket.readyState === WebSocket.OPEN) {
            return activeSocket;
        }

        const arr = Array.from(trackedSockets);
        for (let i = arr.length - 1; i >= 0; i -= 1) {
            const socket = arr[i];
            if (socket.readyState === WebSocket.OPEN) {
                activeSocket = socket;
                return socket;
            }
        }

        return null;
    }

    function hookWebSocket() {
        if (wsInitDone) {
            return;
        }

        wsInitDone = true;
        const NativeWebSocket = window.WebSocket;
        const originalSend = NativeWebSocket.prototype.send;

        window.wsObj = window.wsObj || {};

        function markLikelyGartic(socket, payload) {
            if (typeof payload !== "string") {
                return;
            }
            if (payload.startsWith("42[3,")) {
                socket.__ykLikelyGartic = true;
                activeSocket = socket;
                if (!window.wsObj || typeof window.wsObj.send !== "function") {
                    window.wsObj = socket;
                }
            }
        }

        NativeWebSocket.prototype.send = function (...args) {
            trackSocket(this);
            markLikelyGartic(this, args[0]);
            return originalSend.apply(this, args);
        };

        const WrappedWebSocket = function (...args) {
            const sock = new NativeWebSocket(...args);
            trackSocket(sock);
            return sock;
        };

        WrappedWebSocket.prototype = NativeWebSocket.prototype;
        Object.setPrototypeOf(WrappedWebSocket, NativeWebSocket);
        window.WebSocket = WrappedWebSocket;
    }

    function rememberSelfEcho(text) {
        recentSelfEcho.push({ text, ts: Date.now() });
        if (recentSelfEcho.length > 30) {
            recentSelfEcho.splice(0, recentSelfEcho.length - 30);
        }
    }

    function isSelfEcho(sender, text) {
        const sid = String(sender ?? "");
        const mine = String(myUserId ?? "");
        const room = String(roomOrUserId ?? "");
        const now = Date.now();

        while (recentSelfEcho.length && now - recentSelfEcho[0].ts > 5000) {
            recentSelfEcho.shift();
        }

        const matchesRecent = recentSelfEcho.some((x) => x.text === text && now - x.ts <= 5000);
        if (!matchesRecent) {
            return false;
        }

        return sid === mine || sid === room;
    }

    function boot() {
        createUI();
        applyChatColor();
        enableSelectionBypass();
        hookWebSocket();
        updateAntiAfkTimer();
        setInterval(tickAnswerAwaitResend, 450);
        window.addEventListener("pagehide", function () {
            maybeAutoExportChat();
        });
        document.addEventListener("visibilitychange", function () {
            if (document.visibilityState === "hidden") {
                maybeAutoExportChat();
            }
        });
        window.addEventListener("beforeunload", function () {
            maybeAutoExportChat();
        });
    }

    function enableSelectionBypass() {
        const style = document.createElement("style");
        style.textContent = `
            #${PANEL_ID}, #${PANEL_ID} * {
                -webkit-user-select: text !important;
                -moz-user-select: text !important;
                -ms-user-select: text !important;
                user-select: text !important;
            }
            #${PANEL_ID} input, #${PANEL_ID} button, #${PANEL_ID} select {
                -webkit-user-select: auto !important;
                user-select: auto !important;
            }
        `;
        document.head.appendChild(style);

        document.addEventListener("selectstart", function (event) {
            const target = event.target;
            if (target && target.closest && target.closest(`#${PANEL_ID}`)) {
                event.stopImmediatePropagation();
            }
        }, true);
    }

    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", boot);
    } else {
        boot();
    }
})();