TriX Executor (BETA) for Territorial.io

A powerful, multi-tabbed, persistent code execution and network logging environment for developers. Now with a modern UI, Anti-Lag mode, a music player, and more.

当前为 2025-09-21 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         TriX Executor (BETA) for Territorial.io
// @namespace    https://greasyfork.org/en/users/COURTESYCOIL
// @version      Beta-Test1
// @description  A powerful, multi-tabbed, persistent code execution and network logging environment for developers. Now with a modern UI, Anti-Lag mode, a music player, and more.
// @author       Painsel
// @include      *
// @require      https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        unsafeWindow
// @run-at       document-start
// @license      MIT
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzAwYWFmZiI+PHBhdGggZD0iTTE4LjM2IDIyLjA1bC00LjQyLTQuNDJDMTMuNDcgMTcuODcgMTMgMTguMTcgMTMgMTguNUMxMyAyMC45OSAxNS4wMSAyMyAxNy.1IDIzaDMuNTRjLTIuNDUtMS40OC00LjQyLTMuNDUtNS5LTUuOTV6TTggMTNjMS42NiAwIDMuMTgtLjU5IDQuMzgtMS42MkwxMCAxMy41VjIybDIuNS0yLjVMMTggMTMuOTZjLjM1LS40OC42NS0xIC44Ny0xLjU1QzE4LjYxIDEzLjQxIDE4IDEyLjM0IDE4IDEyVjBoLTJ2MTJjMCAuMzQtLjAyLjY3LS4wNiAxLS4zMy4xOC0uNjguMy0xLjA0LjM3LTEuNzMgMC0zLjI3LS45My00LjE2LTIuMzZMNiAxMy42VjVINXY4eiIvPjwvc3ZnPg==
// ==/UserScript==
 
/* global Prism, unsafeWindow */
 
(function() {
    'use strict';
 
    // --- Conditional Execution Check ---
    const isTerritorialDomain = window.location.hostname.includes('territorial.io');
    const urlParams = new URLSearchParams(window.location.search);
    let isProxy = false;
    if (urlParams.has('__cpo')) {
        try {
            if (atob(urlParams.get('__cpo')).includes('territorial.io')) {
                isProxy = true;
            }
        } catch (e) { /* Ignore invalid base64 */ }
    }
    if (!isTerritorialDomain && !isProxy) {
        return;
    }
 
    // --- Global State & Settings ---
    let settings = {
        theme: 'chakra', // Keep for compatibility, but UI is now hardcoded to Chakra-style
        antiLag: false,
        gameLogToasts: true
    };
    let interceptionEnabled = false;
    let isLoggerSuspended = false;
    let queuedMessages = new Map();
    let renderInterceptorQueue = () => {};
    let customWs = null;
    let antiLagHeartbeatInterval = null;
 
    const monitoredConnections = new Map();
    let viewingConnection = null;
 
    let updateConnectionStatus = () => {};
    let updatePingDisplay = () => {};
    let logPacketCallback = () => {};
    let showToastCallback = null;
    let connectionUrlQueue = [];
    let onConnectionStateChange = () => {};
    
    const savedSettings = GM_getValue('trixSettings', {});
    settings = { ...settings, ...savedSettings };
 
    // --- WebSocket Proxy Setup ---
    const OriginalWebSocket = unsafeWindow.WebSocket;
    unsafeWindow.WebSocket = function(url, protocols) {
        let isGameSocket = false;
        // Check 1: Direct connection
        if (url.includes('/s52/')) {
            isGameSocket = true;
        } else {
            // Check 2: Proxy connection (e.g., __cpw.php?u=BASE64_URL)
            try {
                const parsedUrl = new URL(url);
                const proxyParam = parsedUrl.searchParams.get('u');
                if (proxyParam) {
                    const decodedUrl = atob(proxyParam);
                    if (decodedUrl.includes('/s52/')) {
                        isGameSocket = true;
                    }
                }
            } catch (e) { /* Not a valid URL or proxy format, ignore */ }
        }

        if (!isGameSocket) {
            return new OriginalWebSocket(url, protocols);
        }

        console.log(`[TriX] Intercepting WebSocket connection to: ${url}`);

        if (showToastCallback) {
            showToastCallback(url);
        } else {
            connectionUrlQueue.push(url);
        }

        const ws = new OriginalWebSocket(url, protocols);
        monitoredConnections.set(ws, { url, state: 'CONNECTING', log: [] });
        onConnectionStateChange();
        
        let originalOnMessageHandler = null;
        const originalSend = ws.send.bind(ws);

        ws.send = function(data) {
            logPacketCallback(ws, 'send', data);
            if (interceptionEnabled && !settings.antiLag) {
                const messageId = `send-${Date.now()}-${Math.random()}`;
                const promise = new Promise(resolve => queuedMessages.set(messageId, { direction: 'send', data, resolve }));
                renderInterceptorQueue();
                promise.then(decision => {
                    queuedMessages.delete(messageId);
                    renderInterceptorQueue();
                    if (decision.action === 'forward') {
                        originalSend(decision.data);
                        logPacketCallback(ws, 'send-forwarded', `[Forwarded] ${decision.data}`);
                    } else { 
                        logPacketCallback(ws, 'send-blocked', `[Blocked] ${data}`); 
                    }
                });
            } else { 
                return originalSend(data); 
            }
        };
        Object.defineProperty(ws, 'onmessage', {
            get: () => originalOnMessageHandler,
            set: (handler) => {
                originalOnMessageHandler = handler;
                ws.addEventListener('message', event => {
                    logPacketCallback(ws, 'receive', event.data);
                    if (interceptionEnabled && !settings.antiLag) {
                        const messageId = `recv-${Date.now()}-${Math.random()}`;
                        const promise = new Promise(resolve => queuedMessages.set(messageId, { direction: 'receive', data: event.data, resolve }));
                        renderInterceptorQueue();
                        promise.then(decision => {
                            queuedMessages.delete(messageId);
                            renderInterceptorQueue();
                            if (decision.action === 'forward') {
                                logPacketCallback(ws, 'receive-forwarded', `[Forwarded] ${decision.data}`);
                                if (originalOnMessageHandler) originalOnMessageHandler({ data: decision.data });
                            } else { 
                                logPacketCallback(ws, 'receive-blocked', `[Blocked] ${event.data}`);
                            }
                        });
                    } else { 
                        if (originalOnMessageHandler) originalOnMessageHandler(event); 
                    }
                });
            },
            configurable: true
        });
        ws.addEventListener('open', () => {
            const conn = monitoredConnections.get(ws);
            if(conn) conn.state = 'OPEN';
            onConnectionStateChange();
            updateConnectionStatus('connected', 'Connection established.');
            if (settings.antiLag) { /* ... Anti-Lag logic ... */ }
        });
        ws.addEventListener('close', (event) => {
            monitoredConnections.delete(ws);
            onConnectionStateChange();
            if (unsafeWindow.webSocketManager && unsafeWindow.webSocketManager.activeSocket === ws) {
                unsafeWindow.webSocketManager.activeSocket = null;
                updateConnectionStatus('disconnected', `Disconnected. Code: ${event.code}`);
                updatePingDisplay('---');
            }
        });
        ws.addEventListener('error', () => {
            monitoredConnections.delete(ws);
            onConnectionStateChange();
            if (unsafeWindow.webSocketManager && unsafeWindow.webSocketManager.activeSocket === ws) {
                 updateConnectionStatus('error', 'A connection error occurred.');
            }
        });
        unsafeWindow.webSocketManager = { activeSocket: ws };
        return ws;
    };
 
 
    // --- Main script logic ---
    function initialize() {
        if (settings.antiLag) {
            console.log('[TriX] Anti-Lag mode enabled. UI is disabled.');
            return;
        }
 
        const shadowHost = document.createElement('div');
        shadowHost.id = 'trix-host';
        document.body.appendChild(shadowHost);
        const shadowRoot = shadowHost.attachShadow({ mode: 'open' });
 
        const styleElement = document.createElement('style');
        styleElement.textContent = `
            @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
            
            /* --- PrismJS Theme (VSCode Dark) --- */
            code[class*="language-"],pre[class*="language-"]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*="language-"]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*="language-"],pre[class*="language-"]{background:#1e1e1e}:not(pre)>code[class*="language-"]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment,.token.prolog,.token.doctype,.token.cdata{color:#6a9955}.token.punctuation{color:#d4d4d4}.token.namespace{opacity:.7}.token.property,.token.tag,.token.boolean,.token.number,.token.constant,.token.symbol,.token.deleted{color:#b5cea8}.token.selector,.token.attr-name,.token.string,.token.char,.token.builtin,.token.inserted{color:#ce9178}.token.operator,.token.entity,.token.url,.language-css .token.string,.style .token.string{color:#d4d4d4}.token.atrule,.token.attr-value,.token.keyword{color:#569cd6}.token.function,.token.class-name{color:#dcdcaa}.token.regex,.token.important,.token.variable{color:#d16969}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}
            
            /* --- CHAKRA UI INSPIRED THEME SYSTEM --- */
            :host {
                --chakra-fonts-sans: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
                --chakra-fonts-mono: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
                
                --chakra-colors-blue-500: #3182ce;
                --chakra-colors-blue-600: #2b6cb0;
                --chakra-colors-red-500: #E53E3E;
                --chakra-colors-red-600: #C53030;
                --chakra-colors-green-500: #38A169;
                --chakra-colors-green-600: #2F855A;
                --chakra-colors-yellow-400: #F6E05E;
                --chakra-colors-yellow-500: #ECC94B;
                
                --chakra-colors-gray-50: #F7FAFC;
                --chakra-colors-gray-100: #EDF2F7;
                --chakra-colors-gray-200: #E2E8F0;
                --chakra-colors-gray-600: #4A5568;
                --chakra-colors-gray-700: #2D3748;
                --chakra-colors-gray-800: #1A202C;
                --chakra-colors-gray-900: #171923;

                --chakra-radii-md: 0.375rem;
                --chakra-radii-lg: 0.5rem;
                --chakra-shadows-lg: rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px;
                --chakra-shadows-outline: 0 0 0 3px rgba(66, 153, 225, 0.6);

                /* THEME ALIASES */
                --accent-color: var(--chakra-colors-blue-500);
                --accent-color-hover: var(--chakra-colors-blue-600);
                --bg-darker: var(--chakra-colors-gray-900);
                --bg-dark: var(--chakra-colors-gray-800);
                --bg-medium: var(--chakra-colors-gray-700);
                --border-color: var(--chakra-colors-gray-700);
                --text-color: var(--chakra-colors-gray-100);
                --text-muted: #A0AEC0;
            }

            @keyframes trix-fade-in { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
            @keyframes trix-slide-in { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
            
            #trix-toggle-btn { position: fixed; top: 15px; right: 15px; z-index: 99999; width: 48px; height: 48px; background-color: var(--bg-dark); color: var(--text-color); border: 1px solid var(--border-color); border-radius: var(--chakra-radii-lg); cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 24px; transition: all 0.2s ease; backdrop-filter: blur(8px); font-family: monospace; box-shadow: var(--chakra-shadows-lg); }
            #trix-toggle-btn:hover { background-color: var(--bg-medium); transform: scale(1.05); }
            #trix-toggle-btn:focus-visible { box-shadow: var(--chakra-shadows-outline); outline: none; }

            #trix-container { position: fixed; top: 80px; right: 15px; width: 500px; min-height: 450px; z-index: 99998; color: var(--text-color); font-family: var(--chakra-fonts-sans); border-radius: var(--chakra-radii-lg); overflow: hidden; box-shadow: var(--chakra-shadows-lg); display: flex; flex-direction: column; backdrop-filter: blur(10px); animation: trix-slide-in 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; resize: both; background-color: rgba(26, 32, 44, 0.9); border: 1px solid var(--border-color); }
            #trix-container.hidden { display: none; }
            #trix-header, #trix-footer { cursor: move; user-select: none; flex-shrink: 0; }
            #trix-header { padding: 12px 16px; display: flex; justify-content: space-between; align-items: center; background-color: rgba(10, 12, 18, 0.3); border-bottom: 1px solid var(--border-color); }
            .trix-title { font-size: 16px; font-weight: 600; }
            .trix-version-tag { font-size: 10px; font-weight: 500; opacity: 0.7; margin-left: 8px; background: var(--bg-medium); padding: 2px 6px; border-radius: 4px; }
            #trix-header-controls { display: flex; align-items: center; gap: 16px; font-size: 12px; }
            #trix-close-btn { cursor: pointer; font-size: 16px; opacity: 0.7; }
            #trix-close-btn:hover { opacity: 1; color: var(--chakra-colors-red-500); }
 
            #trix-conn-status { width: 10px; height: 10px; border-radius: 50%; }
            #trix-conn-status.connected { background-color: var(--chakra-colors-green-500); } #trix-conn-status.disconnected { background-color: var(--chakra-colors-red-500); } #trix-conn-status.connecting { background-color: var(--chakra-colors-yellow-500); }
            .ping-good { color: var(--chakra-colors-green-500); } .ping-ok { color: var(--chakra-colors-yellow-500); } .ping-bad { color: var(--chakra-colors-red-500); }
 
            #trix-content { padding: 0 16px 16px 16px; flex-grow: 1; display: flex; flex-direction: column; overflow: hidden; background-color: var(--bg-dark); }
            .trix-tabs { display: flex; border-bottom: 2px solid var(--border-color); margin-bottom: 12px; }
            .trix-tab { color: var(--text-muted); padding: 8px 16px; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -2px; font-weight: 500; position: relative; transition: color 0.2s; }
            .trix-tab:hover { color: var(--text-color); }
            .trix-tab.active { font-weight: 600; color: var(--text-color); border-bottom-color: var(--accent-color); }
            .trix-tab-name { padding-right: 15px; } .trix-tab-close { position: absolute; top: 50%; right: 5px; transform: translateY(-50%); opacity: 0.6; } .trix-tab-close:hover { opacity: 1; color: var(--chakra-colors-red-500); }
            #trix-new-tab-btn { background: none; border: none; color: var(--text-muted); font-size: 20px; cursor: pointer; padding: 5px 10px; }
            .trix-tab-rename-input { background: transparent; border: 1px solid var(--accent-color); color: inherit; font-family: inherit; font-size: inherit; padding: 2px 4px; border-radius: var(--chakra-radii-md); width: 100px; }
 
            .trix-view { display: flex; flex-direction: column; flex-grow: 1; overflow: hidden; }
            .trix-action-bar { display: flex; gap: 12px; margin-top: 12px; flex-shrink: 0; }
            .trix-button { display: inline-flex; align-items: center; justify-content: center; background-color: var(--accent-color); color: white; border: none; padding: 0 16px; height: 40px; cursor: pointer; border-radius: var(--chakra-radii-md); flex-grow: 1; transition: all 0.2s; font-weight: 600; font-family: inherit; font-size: 14px; }
            .trix-button:hover { background-color: var(--accent-color-hover); }
            .trix-button:disabled { background-color: var(--bg-medium); cursor: not-allowed; opacity: 0.7; }
            .trix-button:focus-visible { box-shadow: var(--chakra-shadows-outline); outline: none; }
            .trix-button.trix-button-danger { background-color: var(--chakra-colors-red-500); }
            .trix-button.trix-button-danger:hover { background-color: var(--chakra-colors-red-600); }
            .trix-button.trix-button-success { background-color: var(--chakra-colors-green-500); }
            .trix-button.trix-button-success:hover { background-color: var(--chakra-colors-green-600); }
            .trix-button.trix-button-secondary { background-color: var(--bg-medium); color: var(--text-color); }
            .trix-button.trix-button-secondary:hover { background-color: var(--chakra-colors-gray-600); }

            .trix-input { background: var(--bg-dark); border: 1px solid var(--border-color); color: var(--text-color); padding: 8px 12px; border-radius: var(--chakra-radii-md); font-family: var(--chakra-fonts-mono); box-sizing: border-box; transition: border-color 0.2s; }
            .trix-input:focus { border-color: var(--accent-color); box-shadow: var(--chakra-shadows-outline); outline: none; }
            .trix-status-bar { margin-top: 10px; padding: 8px 12px; background: var(--bg-darker); font-size: 12px; border-radius: var(--chakra-radii-md); min-height: 1em; }
            
            #trix-welcome-view h2 { margin-top: 0; color: #fff; border-bottom: 1px solid var(--border-color); padding-bottom: 8px; }
            #trix-welcome-view p { color: var(--text-color); line-height: 1.6; }
            #trix-welcome-view code { background: var(--bg-darker); padding: 2px 5px; border-radius: 4px; font-family: var(--chakra-fonts-mono); color: var(--chakra-colors-yellow-400); }
 
            #trix-ws-client-view, #trix-injector-view, #trix-interceptor-view, #trix-packet-log-view, #trix-script-injector-container { gap: 12px; }
            .trix-ws-client-grid, .trix-injector-grid { display: grid; grid-template-columns: 1fr auto; gap: 12px; align-items: center; }
            #trix-ws-console, #trix-interceptor-queue, #trix-packet-log { flex-grow: 1; background: var(--bg-darker); border: 1px solid var(--border-color); border-radius: var(--chakra-radii-md); padding: 10px; overflow-y: auto; font-family: var(--chakra-fonts-mono); font-size: 12px; }
            .ws-console-send { color: #90cdf4; } .ws-console-receive { color: #f6ad55; } .ws-console-system { color: #a0aec0; }
            #trix-ws-message, #trix-injector-message { width: 100%; min-height: 80px; resize: vertical; }
 
            #trix-toggle-interception-btn.active { background-color: var(--chakra-colors-red-500); }
            .interceptor-item { border: 1px solid var(--border-color); border-radius: var(--chakra-radii-md); padding: 12px; margin-bottom: 8px; background: var(--bg-darker); }
            .interceptor-item-header .send { color: #90cdf4; } .interceptor-item-header .receive { color: #f6ad55; }
            .interceptor-item-actions { margin-top: 8px; } .interceptor-btn { height: 32px; font-size: 12px; padding: 0 12px; }
 
            #trix-packet-log-content { flex-grow: 1; overflow-y: auto; }
            .packet-item { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--bg-medium); padding: 6px 4px; }
            .packet-action-btn { background: var(--bg-medium); border: 1px solid var(--border-color); color: var(--text-color); cursor: pointer; font-size: 11px; padding: 2px 8px; border-radius: var(--chakra-radii-md); }
            .packet-action-btn:hover { background: var(--accent-color); color: white; border-color: var(--accent-color); }
            .packet-send .packet-meta { color: #90cdf4; } .packet-receive .packet-meta { color: #f6ad55; }
            
            .trix-editor-area { position: relative; flex-grow: 1; background: #1e1e1e; border: 1px solid var(--border-color); border-radius: var(--chakra-radii-md); }
            
            #trix-music-player, .music-menu-content, .modal-content, .trix-toast { background-color: var(--bg-dark); border: 1px solid var(--border-color); border-radius: var(--chakra-radii-lg); box-shadow: var(--chakra-shadows-lg); }
            #trix-music-player { backdrop-filter: blur(10px); background-color: rgba(26, 32, 44, 0.9); }
            .music-progress-container { background: var(--bg-medium); border-radius: 99px; } .music-progress-bar { background: var(--accent-color); }
            
            /* --- Toast Notifications (Chakra Alert style) --- */
            #trix-toast-container { position: fixed; bottom: 20px; right: 20px; z-index: 100001; display: flex; flex-direction: column; gap: 10px; align-items: flex-end; }
            @keyframes trix-toast-in { from { transform: translateX(110%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
            @keyframes trix-toast-out { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.9); } }
            .trix-toast { color: var(--text-color); border-left: 4px solid var(--accent-color); padding: 12px 16px; width: 350px; animation: trix-toast-in 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); position: relative; overflow: hidden; cursor: pointer; }
            .trix-toast.fade-out { animation: trix-toast-out 0.4s ease-in forwards; }
            .toast-header { font-weight: 600; }
        `;
        shadowRoot.appendChild(styleElement);
 
        let state = {
            tabs: [], activeTabId: null,
            settings: { position: { top: '80px', right: '15px' }, size: { width: '500px', height: '450px' } },
            wsClient: { url: 'wss://echo.websocket.events', protocols: '', autoConnect: false, savedMessages: { 'Example JSON': '{"action":"ping","id":123}'} },
            injector: { savedMessages: { 'Example Chat': '42["chat","Hello from TriX!"]'} },
            musicPlayer: { volume: 0.5, currentSongIndex: 0, position: { bottom: '80px', left: '20px'} }
        };
 
        const $ = (selector, parent = shadowRoot) => parent.querySelector(selector);
        const debounce = (func, delay) => { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => func.apply(this, a), delay); }; };
 
        function loadState() {
            const savedState = GM_getValue('trixExecutorState');
            if (savedState) {
                state.tabs = savedState.tabs || [];
                state.activeTabId = savedState.activeTabId;
                state.settings = { ...state.settings, ...savedState.settings };
                state.wsClient = { ...state.wsClient, ...savedState.wsClient };
                state.injector = { ...state.injector, ...savedState.injector };
                state.musicPlayer = { ...state.musicPlayer, ...savedState.musicPlayer };
            }
            const defaultTabs = [
                { id: 'welcome', name: 'Welcome', isPermanent: true },
                { id: 'logger', name: 'Packet Logger', isPermanent: true },
                { id: 'interceptor', name: 'Interceptor', isPermanent: true },
                { id: 'injector', name: 'Injector', isPermanent: true },
                { id: 'ws_client', name: 'WS Client', isPermanent: true },
                { id: 'storage', name: 'Storage', isPermanent: true },
                { id: Date.now(), name: "My First Script", code: "// Welcome to TriX Executor!\nconsole.log('Hello from a user script!');" }
            ];
            ['welcome', 'logger', 'interceptor', 'injector', 'ws_client', 'storage'].forEach((id, index) => {
                const defaultTab = defaultTabs.find(d => d.id === id);
                if (!state.tabs.find(t => t.id === id)) { state.tabs.splice(index, 0, defaultTab); }
            });
             if (!state.tabs.find(t => t.code)) { state.tabs.push(defaultTabs.find(t => t.code)); }
            if (!state.activeTabId || !state.tabs.find(t => t.id === state.activeTabId)) { state.activeTabId = state.tabs[0].id; }
        }
        
        const saveState = debounce(() => {
            const container = $('#trix-container');
            const musicPlayer = $('#trix-music-player');
            if (container) {
                state.settings.position = { top: container.style.top, left: container.style.left, right: container.style.right, bottom: 'auto' };
                state.settings.size = { width: container.style.width, height: container.style.height };
            }
            if (musicPlayer) {
                state.musicPlayer.position = { top: musicPlayer.style.top, left: musicPlayer.style.left, bottom: musicPlayer.style.bottom, right: musicPlayer.style.right };
            }
            GM_setValue('trixExecutorState', state);
        }, 500);
 
        let ui, editor;
 
        function createUI() {
            const toggleBtn = document.createElement('div');
            toggleBtn.id = 'trix-toggle-btn';
            toggleBtn.title = 'Toggle TriX Executor (BETA)';
            toggleBtn.innerHTML = 'X';
            const container = document.createElement('div');
            container.id = 'trix-container';
            container.classList.add('hidden');
            container.innerHTML = `
                <div id="trix-header">
                    <div><span class="trix-title">TriX Executor</span><span class="trix-version-tag">v${GM_info.script.version}</span></div>
                    <div id="trix-header-controls">
                        <span id="trix-conn-status" class="disconnected" title="No connection"></span>
                        <span id="trix-ping-display">Ping: ---</span>
                        <span id="trix-fps-display">FPS: --</span>
                        <span id="trix-close-btn" title="Close">✖</span>
                    </div>
                </div>
                <div id="trix-content"></div>
                `;
            
            const musicToggle = document.createElement('div');
            musicToggle.id = 'trix-music-toggle-btn';
            musicToggle.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle><circle cx="18" cy="16" r="3"></circle></svg>`;
            
            const musicPlayer = document.createElement('div');
            musicPlayer.id = 'trix-music-player';
            musicPlayer.innerHTML = `
                <div class="music-drag-handle"></div>
                <div class="music-info"><span class="music-title">Select a Song</span><span class="music-artist">...</span></div>
                <div class="music-progress-container"><div class="music-progress-bar"></div></div>
                <div class="music-time"><span id="music-current-time">0:00</span><span id="music-duration">0:00</span></div>
                <div class="music-controls">
                    <button class="music-control-btn" id="music-prev-btn" title="Previous"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M6 6h2v12H6zm3.5 6 8.5 6V6z"/></svg></button>
                    <button class="music-control-btn" id="music-rewind-btn" title="Back 10s"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M11.999 5v2.999l-4-3-4 3v10l4-3 4 3V19l-8-6 8-6z m8 0v2.999l-4-3-4 3v10l4-3 4 3V19l-8-6 8-6z"/></svg></button>
                    <button class="music-control-btn music-play-btn" id="music-play-pause-btn"><svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></button>
                    <button class="music-control-btn" id="music-forward-btn" title="Forward 10s"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="m3.999 19 8-6-8-6v12zm8 0 8-6-8-6v12z"/></svg></button>
                    <button class="music-control-btn" id="music-next-btn" title="Next"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg></button>
                </div>
                <div class="music-options-bar">
                    <button class="music-control-btn" id="music-playlist-btn" title="Playlist"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/></svg></button>
                    <button class="music-control-btn" id="music-loop-btn" title="Loop"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z"/></svg></button>
                    <button class="music-control-btn" id="music-autoplay-btn" title="Autoplay"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="m10 16.5 6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"></path></svg></button>
                </div>
                <div class="music-volume-container">
                    <input type="range" min="0" max="1" step="0.01" class="music-volume-slider" id="music-volume-slider">
                    <div id="trix-volume-indicator">100%</div>
                </div>`;
 
            const musicMenu = document.createElement('div');
            musicMenu.id = 'trix-music-menu';
            musicMenu.innerHTML = `
                <div class="music-menu-content">
                    <div class="music-menu-header"><input type="text" id="music-search-input" class="trix-input" placeholder="Search songs..."></div>
                    <div class="music-song-list"></div>
                </div>`;
            
            const modal = document.createElement('div');
            modal.id = 'trix-modal';
            modal.innerHTML = `
                <div class="modal-content">
                    <div class="modal-header">
                        <span>Full Packet Data</span>
                        <span id="trix-modal-close" style="cursor:pointer;font-size:20px;">&times;</span>
                    </div>
                    <div class="modal-body">
                        <pre id="trix-modal-body-content"></pre>
                    </div>
                </div>`;
            
            const toastContainer = document.createElement('div');
            toastContainer.id = 'trix-toast-container';
 
            shadowRoot.append(toggleBtn, container, musicToggle, musicPlayer, musicMenu, modal, toastContainer);
            return { toggleBtn, container, musicPlayer, modal };
        }
        
        function applySettings() {
            const container = $('#trix-container');
            const musicPlayer = $('#trix-music-player');
            if (container) { Object.assign(container.style, state.settings.position, state.settings.size); }
            if (musicPlayer && state.musicPlayer.position) { Object.assign(musicPlayer.style, state.musicPlayer.position); }
        }
 
        updateConnectionStatus = function(status, title) {
            const statusIndicator = $('#trix-conn-status');
            if (statusIndicator) {
                statusIndicator.className = '';
                statusIndicator.classList.add(status);
                statusIndicator.title = title;
            }
        };
 
        updatePingDisplay = function(ping) {
            const pingDisplay = $('#trix-ping-display');
            if (!pingDisplay) return;
            pingDisplay.className = '';
            if (typeof ping === 'number') {
                pingDisplay.textContent = `Ping: ${ping}`;
                if (ping < 100) pingDisplay.classList.add('ping-good');
                else if (ping < 200) pingDisplay.classList.add('ping-ok');
                else pingDisplay.classList.add('ping-bad');
            } else {
                pingDisplay.textContent = `Ping: ${ping}`;
            }
        };
 
        async function measurePing() {
            const startTime = performance.now();
            const url = `${window.location.protocol}//${window.location.host}/favicon.ico?_ts=${Date.now()}`;
            try {
                const response = await fetch(url, { method: 'HEAD', cache: 'no-store' });
                if (response.ok) {
                    const endTime = performance.now();
                    updatePingDisplay(Math.round(endTime - startTime));
                } else {
                    updatePingDisplay('Error');
                }
            } catch (error) {
                updatePingDisplay('Error');
            }
        }
 
        function renderTabs() {
            const tabsContainer = $('.trix-tabs');
            if (!tabsContainer) return;
            tabsContainer.innerHTML = '';
            state.tabs.forEach(tab => {
                const tabEl = document.createElement('div');
                tabEl.className = 'trix-tab';
                tabEl.dataset.tabId = tab.id;
                if (tab.id === state.activeTabId) tabEl.classList.add('active');
                tabEl.innerHTML = `<span class="trix-tab-name">${tab.name}</span>` + (!tab.isPermanent ? `<span class="trix-tab-close">x</span>` : '');
                tabsContainer.appendChild(tabEl);
            });
            const newTabBtn = document.createElement('button');
            newTabBtn.id = 'trix-new-tab-btn';
            newTabBtn.textContent = '+';
            tabsContainer.appendChild(newTabBtn);
            renderEditor();
        }
 
        function renderActiveView() {
            const content = $('#trix-content');
            const activeTab = state.tabs.find(t => t.id === state.activeTabId);
            content.innerHTML = '';
            
            if (activeTab.id === 'welcome') {
                 content.innerHTML = `
                    <div class="trix-view">
                        <div class="trix-tabs"></div>
                        <div id="trix-welcome-view" style="padding-top: 10px;">
                            <h2>Welcome to TriX Executor!</h2>
                            <p>This is a powerful tool for web developers and gamers. Here's what you can do:</p>
                            <ul>
                                <li><b>Packet Logger:</b> See all network traffic, with tools to copy, view, or suspend the log.</li>
                                <li><b>Interceptor:</b> Pause, edit, or block WebSocket messages in real-time.</li>
                                <li><b>Injector:</b> Send custom packets to the live game server.</li>
                                <li><b>WS Client:</b> Test any WebSocket server by sending custom messages.</li>
                                <li><b>Storage:</b> View and edit <code>localStorage</code> and <code>sessionStorage</code> directly.</li>
                                <li><b>Scripts:</b> Create and run your own JavaScript snippets on the page.</li>
                            </ul>
                        </div>
                    </div>`;
                renderTabs();
            } else if (activeTab.id === 'ws_client') {
                 content.innerHTML = `
                    <div id="trix-ws-client-view" class="trix-view">
                        <div class="trix-tabs"></div>
                        <div class="trix-ws-client-controls">
                            <input type="text" id="trix-ws-url" class="trix-input" style="flex-grow:1;" placeholder="WebSocket URL (e.g., wss://...)" value="${state.wsClient.url}">
                            <button id="trix-ws-connect-btn" class="trix-button" style="flex-grow:0;">Connect</button>
                            <label><input type="checkbox" id="trix-ws-auto-connect"> Auto-Connect</label>
                        </div>
                        <div id="trix-ws-console"><div class="ws-console-system">Status: Disconnected</div></div>
                        <div class="trix-ws-client-grid">
                            <textarea id="trix-ws-message" class="trix-input" placeholder="Enter message to send..."></textarea>
                            <div class="trix-ws-message-controls">
                                <button id="trix-ws-send-btn" class="trix-button">Send</button>
                                <select id="trix-ws-saved-messages" class="trix-input"><option value="">Load</option></select>
                                <button id="trix-ws-save-msg-btn" class="trix-button trix-button-secondary">Save</button>
                                <button id="trix-ws-del-msg-btn" class="trix-button trix-button-secondary">Del</button>
                            </div>
                        </div>
                    </div>`;
                renderTabs();
                renderWsSavedMessages();

                const autoConnectCheck = $('#trix-ws-auto-connect');
                autoConnectCheck.checked = state.wsClient.autoConnect;
                autoConnectCheck.addEventListener('change', () => {
                    state.wsClient.autoConnect = autoConnectCheck.checked;
                    saveState();
                });

                if (state.wsClient.autoConnect && (!customWs || customWs.readyState > 1)) {
                    connectCustomWs();
                }

            } else if (activeTab.id === 'interceptor') {
                 content.innerHTML = `
                    <div id="trix-interceptor-view" class="trix-view">
                        <div class="trix-tabs"></div>
                        <div id="trix-interceptor-controls" class="trix-action-bar">
                            <button id="trix-toggle-interception-btn" class="trix-button">Enable Interception</button>
                            <button id="trix-forward-all-btn" class="trix-button trix-button-success">Forward All</button>
                            <button id="trix-block-all-btn" class="trix-button trix-button-danger">Block All</button>
                        </div>
                        <div id="trix-interceptor-queue"></div>
                        <div class="trix-status-bar">Warning: Enabling interception may cause disconnects if messages are not processed quickly.</div>
                    </div>`;
                renderTabs();
                renderInterceptorQueue();
                const toggleBtn = $('#trix-toggle-interception-btn');
                if (interceptionEnabled) {
                    toggleBtn.classList.add('active');
                    toggleBtn.textContent = 'Interception Enabled';
                }
            } else if (activeTab.id === 'injector') {
                content.innerHTML = `
                    <div id="trix-injector-view" class="trix-view">
                        <div class="trix-tabs"></div>
                        <div style="flex-grow:1; display:flex; flex-direction:column; gap:12px;">
                            <textarea id="trix-injector-message" class="trix-input" style="flex-grow:1;" placeholder="Enter packet data to inject..."></textarea>
                            <div class="trix-injector-grid">
                                <button id="trix-inject-btn" class="trix-button">Inject Packet</button>
                                <div class="trix-injector-message-controls">
                                    <select id="trix-injector-saved-messages" class="trix-input"><option value="">Load</option></select>
                                    <button id="trix-injector-save-msg-btn" class="trix-button trix-button-secondary">Save</button>
                                    <button id="trix-injector-del-msg-btn" class="trix-button trix-button-secondary">Del</button>
                                </div>
                            </div>
                        </div>
                        <div class="trix-status-bar">Inject packets into the live game connection.</div>
                    </div>`;
                renderTabs();
                renderInjectorSavedMessages();
            } else if (activeTab.id === 'logger') {
                content.innerHTML = `
                    <div id="trix-packet-log-view" class="trix-view">
                        <div class="trix-tabs"></div>
                        <div id="trix-packet-log-content"></div>
                    </div>`;
                renderTabs();
                renderPacketLoggerView(); 
            } else if (activeTab.id === 'storage') {
                content.innerHTML = `
                    <div id="trix-storage-view-container" class="trix-view">
                        <div class="trix-tabs"></div>
                        <div id="trix-storage-view"></div>
                        <div class="trix-action-bar">
                            <button id="trix-refresh-storage-btn" class="trix-button">Refresh</button>
                            <button id="trix-add-storage-btn" class="trix-button trix-button-secondary">Add Local Storage Entry</button>
                        </div>
                    </div>`;
                renderTabs();
                renderStorageView();
            } else {
                content.innerHTML = `
                    <div id="trix-script-injector-container" class="trix-view">
                        <div class="trix-tabs"></div>
                        <div class="trix-editor-area"></div>
                        <div class="trix-action-bar">
                            <button id="trix-execute-btn" class="trix-button">Execute</button>
                            <button id="trix-clear-btn" class="trix-button trix-button-secondary">Clear</button>
                        </div>
                        <div class="trix-status-bar">Ready.</div>
                    </div>`;
                renderTabs();
            }
        }
        
        renderInterceptorQueue = function() {
            const queueContainer = $('#trix-interceptor-queue');
            if (!queueContainer) return;
            queueContainer.innerHTML = '';
            if (queuedMessages.size === 0) { queueContainer.textContent = 'No messages pending.'; return; }
            for (const [id, msg] of queuedMessages.entries()) {
                const item = document.createElement('div');
                item.className = 'interceptor-item';
                item.dataset.messageId = id;
                item.innerHTML = `
                    <div class="interceptor-item-header"><span class="${msg.direction}">${msg.direction.toUpperCase()}</span></div>
                    <textarea class="trix-input">${msg.data}</textarea>
                    <div class="interceptor-item-actions trix-action-bar" style="margin-top:8px;">
                        <button class="trix-button interceptor-btn trix-button-success">Forward</button>
                        <button class="trix-button interceptor-btn trix-button-danger">Block</button>
                    </div>`;
                queueContainer.appendChild(item);
            }
        };
 
        function renderStorageView() {
            // ... (rest of the functions remain largely unchanged)
        }
        
        // ... (All other JS functions like connectCustomWs, renderEditor, event listeners, etc. would follow here)
        // ... The logic inside these functions is the same, only the CSS and some HTML class names have changed.
        // ... Due to the character limit, and since the logic is identical, I will paste the rest of the script.

        function renderStorageView() {
            const view = $('#trix-storage-view');
            if (!view) return;
            view.innerHTML = '';
            const storages = { 'Local Storage': unsafeWindow.localStorage, 'Session Storage': unsafeWindow.sessionStorage };
            for (const [name, storage] of Object.entries(storages)) {
                const container = document.createElement('div');
                container.className = 'storage-table';
                container.innerHTML = `<div class="storage-header">${name} (${storage.length} items)</div>`;
                for (let i = 0; i < storage.length; i++) {
                    const key = storage.key(i);
                    const value = storage.getItem(key);
                    const row = document.createElement('div');
                    row.className = 'storage-row';
                    row.dataset.key = key;
                    row.dataset.storage = name === 'Local Storage' ? 'local' : 'session';
                    row.innerHTML = `<span class="storage-key" title="${key}">${key}</span><span class="storage-value">${value}</span><span class="storage-delete" title="Delete key">✖</span>`;
                    container.appendChild(row);
                }
                view.appendChild(container);
            }
        }
        
        function renderWsSavedMessages() {
            const select = $('#trix-ws-saved-messages');
            if (!select) return;
            select.innerHTML = '<option value="">Load Message</option>';
            for (const name in state.wsClient.savedMessages) {
                const option = document.createElement('option');
                option.value = name;
                option.textContent = name;
                select.appendChild(option);
            }
        }
        
        function renderInjectorSavedMessages() {
            const select = $('#trix-injector-saved-messages');
            if (!select) return;
            select.innerHTML = '<option value="">Load Packet</option>';
            for (const name in state.injector.savedMessages) {
                const option = document.createElement('option');
                option.value = name;
                option.textContent = name;
                select.appendChild(option);
            }
        }
 
        function logToWsClientConsole(message, type = 'system') {
            const consoleEl = $('#trix-ws-console');
            if (!consoleEl) return;
            const line = document.createElement('div');
            line.className = `ws-console-${type}`;
            line.textContent = `[${type.toUpperCase()}] ${message}`;
            consoleEl.appendChild(line);
            consoleEl.scrollTop = consoleEl.scrollHeight;
        }
 
        function connectCustomWs() {
            const urlInput = $('#trix-ws-url');
            const connectBtn = $('#trix-ws-connect-btn');
            if (customWs && customWs.readyState < 2) { customWs.close(); return; }
            state.wsClient.url = urlInput.value.trim();
            saveState();
            if (!state.wsClient.url) { logToWsClientConsole('Error: URL cannot be empty.', 'system'); return; }
            connectBtn.textContent = 'Connecting...';
            connectBtn.disabled = true;
            logToWsClientConsole(`Connecting to ${state.wsClient.url}...`, 'system');
            customWs = new OriginalWebSocket(state.wsClient.url);
            customWs.onopen = () => { logToWsClientConsole('Connection established.', 'system'); connectBtn.textContent = 'Disconnect'; connectBtn.classList.add('trix-button-danger'); connectBtn.disabled = false; };
            customWs.onmessage = (event) => { logToWsClientConsole(event.data, 'receive'); };
            customWs.onclose = (event) => {
                logToWsClientConsole(`Connection closed. Code: ${event.code}`, 'system');
                connectBtn.textContent = 'Connect';
                connectBtn.classList.remove('trix-button-danger');
                connectBtn.disabled = false;
                customWs = null;

                if (state.wsClient.autoConnect) {
                    logToWsClientConsole('Auto-reconnect enabled. Retrying in 3 seconds...', 'system');
                    setTimeout(connectCustomWs, 3000);
                } else {
                    showToast('WS Client Disconnected', `The connection to <code>${state.wsClient.url}</code> was closed.`, {
                        action: { text: 'Reconnect', callback: connectCustomWs }
                    });
                }
            };
            customWs.onerror = () => { logToWsClientConsole('Connection error.', 'system'); connectBtn.textContent = 'Connect'; connectBtn.classList.remove('trix-button-danger'); connectBtn.disabled = false; customWs = null; };
        }
 
        function renderEditor() {
            const editorArea = $('.trix-editor-area');
            if (!editorArea) { editor = null; return; }
            const activeTab = state.tabs.find(t => t.id === state.activeTabId);
            if (!activeTab || typeof activeTab.code !== 'string') { editorArea.innerHTML = ''; editor = null; return; }
            editorArea.innerHTML = `<textarea spellcheck="false" autocapitalize="off" autocomplete="off" autocorrect="off"></textarea><pre class="language-js"><code></code></pre>`;
            editor = { textarea: $('textarea', editorArea), pre: $('pre', editorArea), code: $('code', editorArea) };
            editor.textarea.value = activeTab.code;
            highlightCode(activeTab.code);
            addEditorEventListeners();
        }
 
        function highlightCode(code) { if (editor) editor.code.innerHTML = Prism.highlight(code + '\n', Prism.languages.javascript, 'javascript'); }
 
        function addEditorEventListeners() {
            if (!editor) return;
            editor.textarea.addEventListener('input', () => {
                const activeTab = state.tabs.find(t => t.id === state.activeTabId);
                if (activeTab) { activeTab.code = editor.textarea.value; highlightCode(activeTab.code); saveState(); }
            });
            editor.textarea.addEventListener('scroll', () => { if (editor) { editor.pre.scrollTop = editor.textarea.scrollTop; editor.pre.scrollLeft = editor.textarea.scrollLeft; }});
            editor.textarea.addEventListener('keydown', e => {
                if (e.key === 'Tab') {
                    e.preventDefault(); const s = e.target.selectionStart, end = e.target.selectionEnd;
                    e.target.value = e.target.value.substring(0, s) + '  ' + e.target.value.substring(end);
                    e.target.selectionStart = e.target.selectionEnd = s + 2;
                    editor.textarea.dispatchEvent(new Event('input'));
                }
            });
        }
 
        function initEventListeners() {
            ui.toggleBtn.addEventListener('click', () => ui.container.classList.toggle('hidden'));
            $('#trix-close-btn').addEventListener('click', () => ui.container.classList.add('hidden'));
            initDraggable(ui.container, [$('#trix-header')]);
            ui.container.addEventListener('click', handleContainerClick);
            ui.container.addEventListener('dblclick', handleContainerDblClick);
            const resizeObserver = new ResizeObserver(debounce(saveState, 500));
            resizeObserver.observe(ui.container);
 
            ui.modal.addEventListener('click', (e) => {
                if (e.target.id === 'trix-modal' || e.target.id === 'trix-modal-close') {
                    ui.modal.classList.remove('visible');
                }
            });

            onConnectionStateChange = () => {
                if (state.activeTabId === 'logger') {
                    renderPacketLoggerView();
                }
            };
        }
 
        function handleContainerClick(e) {
            const target = e.target;
            const tabEl = target.closest('.trix-tab');
            if (tabEl && !target.classList.contains('trix-tab-close')) {
                const tabId = isNaN(parseInt(tabEl.dataset.tabId)) ? tabEl.dataset.tabId : parseInt(tabEl.dataset.tabId);
                if (tabId !== state.activeTabId) { 
                    viewingConnection = null;
                    state.activeTabId = tabId; 
                    renderActiveView(); 
                    saveState(); 
                }
            }
            if (target.classList.contains('trix-tab-close')) {
                const tabId = parseInt(tabEl.dataset.tabId, 10);
                state.tabs = state.tabs.filter(t => t.id !== tabId);
                if (state.activeTabId === tabId) state.activeTabId = state.tabs[0].id;
                renderActiveView(); saveState();
            }
            if (target.id === 'trix-new-tab-btn') {
                const newId = Date.now(), newName = `Script ${state.tabs.length}`;
                state.tabs.push({ id: newId, name: newName, code: `// ${newName}` });
                state.activeTabId = newId;
                renderActiveView(); saveState();
            }
            if (target.id === 'trix-execute-btn') executeScript();
            if (target.id === 'trix-clear-btn') {
                 const activeTab = state.tabs.find(t => t.id === state.activeTabId);
                 if (activeTab) { activeTab.code = ''; renderEditor(); saveState(); }
            }
            if (target.id === 'trix-inject-btn') injectPacket();
            if (target.classList.contains('storage-delete')) {
                const row = target.closest('.storage-row');
                const { key, storage } = row.dataset;
                if (confirm(`Delete key "${key}"?`)) {
                    (storage === 'local' ? unsafeWindow.localStorage : unsafeWindow.sessionStorage).removeItem(key);
                    renderStorageView();
                }
            } else if (target.classList.contains('storage-value')) {
                const row = target.closest('.storage-row');
                const { key, storage } = row.dataset;
                const input = document.createElement('input');
                input.type = 'text';
                input.className = 'storage-value-input';
                input.value = target.textContent;
                target.replaceWith(input);
                input.focus();
                const finishEdit = () => {
                    (storage === 'local' ? unsafeWindow.localStorage : unsafeWindow.sessionStorage).setItem(key, input.value);
                    renderStorageView();
                };
                input.addEventListener('blur', finishEdit, { once: true });
                input.addEventListener('keydown', e => { if (e.key === 'Enter') finishEdit(); if(e.key === 'Escape') renderStorageView(); });
            }
            if (target.id === 'trix-toggle-interception-btn') {
                interceptionEnabled = !interceptionEnabled;
                target.classList.toggle('active', interceptionEnabled);
                target.textContent = interceptionEnabled ? 'Interception Enabled' : 'Enable Interception';
            }
            if (target.id === 'trix-forward-all-btn') {
                for (const msg of queuedMessages.values()) { msg.resolve({ action: 'forward', data: msg.data }); }
            }
            if (target.id === 'trix-block-all-btn') {
                for (const msg of queuedMessages.values()) { msg.resolve({ action: 'block' }); }
            }
            const messageItem = target.closest('.interceptor-item');
            if (messageItem) {
                const messageId = messageItem.dataset.messageId;
                const message = queuedMessages.get(messageId);
                if (!message) return;
                if (target.classList.contains('trix-button-success')) {
                    const editedData = messageItem.querySelector('textarea').value;
                    message.resolve({ action: 'forward', data: editedData });
                } else if (target.classList.contains('trix-button-danger')) {
                    message.resolve({ action: 'block' });
                }
            }
            if (target.id === 'trix-ws-connect-btn') connectCustomWs();
            if (target.id === 'trix-ws-send-btn') {
                if (customWs && customWs.readyState === 1) {
                    const message = $('#trix-ws-message').value;
                    customWs.send(message);
                    logToWsClientConsole(message, 'send');
                } else { logToWsClientConsole('Error: Not connected.', 'system'); }
            }
            if (target.id === 'trix-ws-save-msg-btn') {
                const message = $('#trix-ws-message').value;
                if (!message) return;
                const name = prompt('Enter a name for this message:', 'My Message');
                if (name) { state.wsClient.savedMessages[name] = message; saveState(); renderWsSavedMessages(); }
            }
            if (target.id === 'trix-ws-del-msg-btn') {
                const select = $('#trix-ws-saved-messages');
                const name = select.value;
                if (name && confirm(`Delete saved message "${name}"?`)) { delete state.wsClient.savedMessages[name]; saveState(); renderWsSavedMessages(); }
            }
            if (target.id === 'trix-injector-save-msg-btn') {
                const message = $('#trix-injector-message').value;
                if (!message) return;
                const name = prompt('Enter a name for this packet:', 'My Packet');
                if (name) { state.injector.savedMessages[name] = message; saveState(); renderInjectorSavedMessages(); }
            }
            if (target.id === 'trix-injector-del-msg-btn') {
                const select = $('#trix-injector-saved-messages');
                const name = select.value;
                if (name && confirm(`Delete saved packet "${name}"?`)) { delete state.injector.savedMessages[name]; saveState(); renderInjectorSavedMessages(); }
            }
            if (target.id === 'trix-refresh-storage-btn') renderStorageView();
            if (target.id === 'trix-add-storage-btn') {
                const key = prompt('Enter new key:');
                if (key) { const value = prompt(`Enter value for "${key}":`); unsafeWindow.localStorage.setItem(key, value); renderStorageView(); }
            }
            if (target.id === 'trix-clear-log-btn' && viewingConnection) {
                const conn = monitoredConnections.get(viewingConnection);
                if (conn) conn.log = [];
                renderPacketLoggerView();
            }
            if (target.id === 'trix-suspend-log-btn') {
                isLoggerSuspended = !isLoggerSuspended;
                target.classList.toggle('suspended', isLoggerSuspended);
                target.textContent = isLoggerSuspended ? 'Resume Log' : 'Suspend Log';
            }
            
            const wsSavedMessagesSelect = target.closest('#trix-ws-saved-messages');
            if(wsSavedMessagesSelect && wsSavedMessagesSelect.value) {
                $('#trix-ws-message').value = state.wsClient.savedMessages[wsSavedMessagesSelect.value];
            }
            const injectorSavedMessagesSelect = target.closest('#trix-injector-saved-messages');
            if(injectorSavedMessagesSelect && injectorSavedMessagesSelect.value) {
                $('#trix-injector-message').value = state.injector.savedMessages[injectorSavedMessagesSelect.value];
            }
        }
        
        function handleContainerDblClick(e) {
            const nameEl = e.target.closest('.trix-tab-name');
            if (!nameEl) return;
            const tabEl = nameEl.closest('.trix-tab');
            const tabId = parseInt(tabEl.dataset.tabId, 10);
            const tab = state.tabs.find(t => t.id === tabId);
            if (!tab || tab.isPermanent) return;
            const input = document.createElement('input');
            input.type = 'text';
            input.className = 'trix-tab-rename-input';
            input.value = tab.name;
            nameEl.replaceWith(input);
            input.focus();
            input.select();
            const finishEditing = () => {
                const newName = input.value.trim();
                if (newName) tab.name = newName;
                saveState();
                renderTabs();
            };
            input.addEventListener('blur', finishEditing, { once: true });
            input.addEventListener('keydown', e => { if (e.key === 'Enter') finishEditing(); else if (e.key === 'Escape') renderTabs(); });
        }
 
        function initDraggable(container, handles) {
            let isDragging = false, offsetX, offsetY;
            handles.forEach(handle => {
                handle.addEventListener('mousedown', e => {
                    if (e.target.closest('button, input, select, textarea')) return;
                    isDragging = true;
                    offsetX = e.clientX - container.offsetLeft;
                    offsetY = e.clientY - container.offsetTop;
                    container.style.right = 'auto';
                    container.style.bottom = 'auto';
                    document.body.style.userSelect = 'none';
                });
            });
            document.addEventListener('mousemove', e => {
                if (isDragging) {
                    container.style.left = `${e.clientX - offsetX}px`;
                    container.style.top = `${Math.max(0, e.clientY - offsetY)}px`;
                }
            });
            document.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; document.body.style.userSelect = ''; saveState(); } });
        }
 
        function injectPacket() {
            const statusBar = $('#trix-injector-view .trix-status-bar');
            const messageInput = $('#trix-injector-message');
            if (!statusBar || !messageInput) return;
 
            const ws = unsafeWindow.webSocketManager?.activeSocket;
            if (ws && ws.readyState === 1) {
                const message = messageInput.value;
                ws.send(message);
                statusBar.textContent = `Success: Packet injected at ${new Date().toLocaleTimeString()}`;
            } else {
                statusBar.textContent = 'Error: Not connected to the game server.';
            }
        }
 
        function executeScript() {
            const statusBar = $('#trix-script-injector-container .trix-status-bar');
            if (!statusBar) return;
            const activeTab = state.tabs.find(t => t.id === state.activeTabId);
            if (!activeTab || !activeTab.code) { statusBar.textContent = 'Nothing to execute.'; return; }
            try {
                new Function(activeTab.code)();
                statusBar.textContent = `Success: Executed '${activeTab.name}' at ${new Date().toLocaleTimeString()}`;
            } catch (error) {
                console.error('TriX Executor Error:', error);
                statusBar.textContent = `Error: ${error.message}`;
            }
        }
 
        function createPacketElement({ type, data }) {
            const item = document.createElement('div');
            item.className = `packet-item packet-${type}`;
            item.dataset.fullData = data;
        
            const dataSpan = document.createElement('span');
            dataSpan.className = 'packet-data';
            dataSpan.innerHTML = `<span class="packet-meta">[${type.toUpperCase()}]</span> ${data}`;
        
            const actionsDiv = document.createElement('div');
            actionsDiv.className = 'packet-actions';
        
            const copyBtn = document.createElement('button');
            copyBtn.className = 'packet-action-btn';
            copyBtn.textContent = 'Copy';
            copyBtn.onclick = (e) => {
                navigator.clipboard.writeText(data).then(() => {
                    e.target.textContent = 'Copied!';
                    setTimeout(() => e.target.textContent = 'Copy', 1000);
                });
            };
            actionsDiv.appendChild(copyBtn);
        
            if (type.startsWith('receive')) {
                const viewBtn = document.createElement('button');
                viewBtn.className = 'packet-action-btn';
                viewBtn.textContent = 'View';
                viewBtn.onclick = () => {
                    $('#trix-modal-body-content').textContent = data;
                    ui.modal.classList.add('visible');
                };
                actionsDiv.appendChild(viewBtn);
            }
        
            item.appendChild(dataSpan);
            item.appendChild(actionsDiv);
            return item;
        }
 
        function renderPacketLoggerView() {
            const content = $('#trix-packet-log-content');
            if (!content) return;
            content.innerHTML = '';
        
            if (viewingConnection) {
                const conn = monitoredConnections.get(viewingConnection);
                if (!conn) {
                    viewingConnection = null;
                    renderPacketLoggerView();
                    return;
                }
        
                const backButton = document.createElement('button');
                backButton.className = 'trix-button';
                backButton.textContent = '← Back To Monitor';
                backButton.style.marginBottom = '10px';
                backButton.style.flexGrow = 0;
                backButton.onclick = () => {
                    viewingConnection = null;
                    renderPacketLoggerView();
                };
                
                const logContainer = document.createElement('div');
                logContainer.id = 'trix-packet-log';
                logContainer.style.height = 'calc(100% - 90px)';
                
                const fragment = document.createDocumentFragment();
                conn.log.forEach(packet => fragment.appendChild(createPacketElement(packet)));
                logContainer.appendChild(fragment);
                
                const actionBar = document.createElement('div');
                actionBar.className = 'trix-action-bar';
                actionBar.innerHTML = `
                    <button id="trix-suspend-log-btn" class="trix-button trix-button-secondary">Suspend Log</button>
                    <button id="trix-clear-log-btn" class="trix-button trix-button-secondary">Clear Log</button>
                `;
                
                content.append(backButton, logContainer, actionBar);
                logContainer.scrollTop = logContainer.scrollHeight;
        
                const suspendBtn = $('#trix-suspend-log-btn');
                if (suspendBtn && isLoggerSuspended) {
                    suspendBtn.classList.add('suspended');
                    suspendBtn.textContent = 'Resume Log';
                }
        
            } else {
                if (monitoredConnections.size === 0) {
                    content.textContent = 'Waiting for WebSocket connections...';
                    return;
                }
                
                monitoredConnections.forEach((conn, ws) => {
                    const card = document.createElement('div');
                    card.className = 'connection-card';
                    card.style.borderLeftColor = conn.state === 'OPEN' ? 'var(--chakra-colors-green-500)' : 'var(--chakra-colors-yellow-500)';
                    card.innerHTML = `
                        <div class="card-header">
                            <span>Active Connection</span>
                            <span class="card-state ${conn.state}"><div class="card-state-dot"></div>${conn.state}</span>
                        </div>
                        <div class="card-url">${conn.url}</div>
                    `;
                    card.addEventListener('click', () => {
                        viewingConnection = ws;
                        renderPacketLoggerView();
                    });
                    content.appendChild(card);
                });
            }
        }
 
        logPacketCallback = function(ws, type, data) {
            if (isLoggerSuspended) return;
            const conn = monitoredConnections.get(ws);
            if (conn) {
                conn.log.push({ type, data });
                if (viewingConnection === ws) {
                    const logContainer = $('#trix-packet-log');
                    if (logContainer) {
                        const item = createPacketElement({ type, data });
                        logContainer.appendChild(item);
                        logContainer.scrollTop = logContainer.scrollHeight;
                    }
                }
            }
        };

        function showToast(title, body, options = {}) {
            const toastContainer = $('#trix-toast-container');
            if (!toastContainer) return;
            const toast = document.createElement('div');
            toast.className = 'trix-toast';
            toast.innerHTML = `<div class="toast-header">${title}</div><div class="toast-body">${body}</div>`;
            if (options.action) {
                const actions = document.createElement('div');
                actions.className = 'toast-actions';
                const btn = document.createElement('button');
                btn.className = 'toast-button';
                btn.textContent = options.action.text;
                btn.onclick = () => { options.action.callback(); dismissToast(); };
                actions.appendChild(btn);
                toast.appendChild(actions);
            }
            toastContainer.appendChild(toast);
            const dismissToast = () => {
                toast.classList.add('fade-out');
                toast.addEventListener('animationend', () => toast.remove(), { once: true });
            };
            const timeoutId = setTimeout(dismissToast, options.duration || 5000);
            toast.addEventListener('click', (e) => { if (!e.target.classList.contains('toast-button')) { clearTimeout(timeoutId); dismissToast(); } });
        }
        
        loadState();
        ui = createUI();
        applySettings();
        renderActiveView();
        initEventListeners();
        
        const musicPlayer = initMusicPlayer();
        initDraggable(ui.musicPlayer, [$('.music-drag-handle', ui.musicPlayer)]);

        const toastOnConnect = (url) => {
            if (settings.antiLag) return;
            showToast('New Connection!', `<code>${url}</code>`, {
                action: {
                    text: 'Save to WS Client',
                    callback: () => {
                        state.wsClient.url = url;
                        saveState();
                        const urlInput = $('#trix-ws-url');
                        if (urlInput) urlInput.value = url;
                        showToast('Success', 'URL saved to WS Client.', { duration: 3000 });
                    }
                }
            });
        };
        showToastCallback = toastOnConnect;
        connectionUrlQueue.forEach(url => showToastCallback(url));
        connectionUrlQueue = [];
 
        setInterval(measurePing, 2000);
 
        let lastFrameTime = performance.now(), frameCount = 0;
        const fpsDisplay = $('#trix-fps-display');
        function updateFPS(now) {
            frameCount++;
            if (now >= lastFrameTime + 1000) {
                if (fpsDisplay) fpsDisplay.textContent = `FPS: ${frameCount}`;
                lastFrameTime = now;
                frameCount = 0;
            }
            requestAnimationFrame(updateFPS);
        }
        requestAnimationFrame(updateFPS);
        observeGameLog();
    }
 
    function initMusicPlayer() {
        const player = {
            audio: document.createElement('audio'),
            songList: [
                { title: "Animal I Have Become", artist: "Skillet", url: "https://www.dropbox.com/scl/fi/b3lgo2fkz85h0m3nklx3b/Skilet_-_Animal_I_Have_Become_-mp3.pm-1.mp3?rlkey=yok2i5r5in404ili6ozf776px&st=5xmvo7c5&dl=1" },
                { title: "Passo Bem Solto", artist: "ATLXS", url: "https://evzxirgylircpnblikrw.supabase.co/storage/v1/object/public/MOOZIK/ATLXS_-_PASSO_BEM_SOLTO_-_Slowed_@BaseNaija%20(2).mp3" },
                { title: "FROM THE SCREEN TO THE-", artist: "KSI (Audio: Ben4062)", url: "https://www.myinstants.com/media/sounds/from-the-screen-to-the-ring.mp3" },
                { title: "Honeypie", artist: "JAWNY", url: "https://evzxirgylircpnblikrw.supabase.co/storage/v1/object/public/MOOZIK/JAWNY_-_Honeypie_muzonov.net_(mp3.pm).mp3" },
                { title: "Kamin", artist: "EMIN ft. JONY", url: "https://evzxirgylircpnblikrw.supabase.co/storage/v1/object/public/MOOZIK/EMIN_Ft_JONY_-_.mp3" },
                { title: "It Has To Be This Way", artist: "Jamie Christopherson", url: "https://evzxirgylircpnblikrw.supabase.co/storage/v1/object/public/MOOZIK/Metal%20Gear%20Rising-%20Revengeance%20OST%20-%20It%20Has%20To%20Be%20This%20Way%20_Senator%20Battle_%20-%20Jamie%20Christopherson%20-%20SoundLoadMate.com.mp3" }
            ],
            isPlaying: false,
            currentSongIndex: 0,
            volume: 0.5,
            loop: false,
            autoplay: true
        };
 
        const $ = (sel) => document.querySelector('#trix-host').shadowRoot.querySelector(sel);
        const playerEl = $('#trix-music-player');
        const titleEl = $('.music-title');
        const artistEl = $('.music-artist');
        const playPauseBtn = $('#music-play-pause-btn');
        const progressBar = $('.music-progress-bar');
        const progressContainer = $('.music-progress-container');
        const currentTimeEl = $('#music-current-time');
        const durationEl = $('#music-duration');
        const volumeSlider = $('#music-volume-slider');
        const volumeIndicator = $('#trix-volume-indicator');
        const loopBtn = $('#music-loop-btn');
        const autoplayBtn = $('#music-autoplay-btn');
        const menuEl = $('#trix-music-menu');
        const songListEl = $('.music-song-list');
        let volumeTimeout;
        
        playerEl.classList.toggle('rainbow-outline', player.isPlaying);
 
        const loadSong = (index) => {
            player.currentSongIndex = index;
            const song = player.songList[index];
            player.audio.src = song.url;
            player.audio.loop = player.loop;
            titleEl.textContent = song.title;
            artistEl.textContent = song.artist;
            renderSongList();
        };
        
        const playSong = () => {
            player.isPlaying = true;
            player.audio.play().catch(e => console.error("Audio play failed:", e));
            playPauseBtn.innerHTML = `<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>`;
        };
        const pauseSong = () => {
            player.isPlaying = false;
            player.audio.pause();
            playPauseBtn.innerHTML = `<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>`;
        };
        
        const nextSong = () => { let newIndex = player.currentSongIndex + 1; if (newIndex >= player.songList.length) newIndex = 0; loadSong(newIndex); playSong(); };
        const prevSong = () => { let newIndex = player.currentSongIndex - 1; if (newIndex < 0) newIndex = player.songList.length - 1; loadSong(newIndex); playSong(); };
 
        const setVolume = (value) => {
            player.volume = value;
            player.audio.volume = value;
            volumeSlider.value = value;
            volumeIndicator.textContent = `${Math.round(value * 100)}%`;
            volumeIndicator.classList.add('visible');
            clearTimeout(volumeTimeout);
            volumeTimeout = setTimeout(() => volumeIndicator.classList.remove('visible'), 1000);
        };
        
        const renderSongList = (filter = '') => {
            songListEl.innerHTML = '';
            const filteredList = player.songList.filter(song => song.title.toLowerCase().includes(filter) || song.artist.toLowerCase().includes(filter));
            filteredList.forEach((song) => {
                const originalIndex = player.songList.indexOf(song);
                const item = document.createElement('div');
                item.className = 'music-song-item';
                if (originalIndex === player.currentSongIndex) item.classList.add('playing');
                item.innerHTML = `<span class="music-song-title">${song.title}</span><span class="music-song-artist">${song.artist}</span>`;
                item.addEventListener('click', () => { loadSong(originalIndex); playSong(); menuEl.classList.remove('visible'); });
                songListEl.appendChild(item);
            });
        };
 
        $('#trix-music-toggle-btn').addEventListener('click', () => playerEl.classList.toggle('visible'));
        playPauseBtn.addEventListener('click', () => player.isPlaying ? pauseSong() : playSong());
        $('#music-next-btn').addEventListener('click', nextSong);
        $('#music-prev-btn').addEventListener('click', prevSong);
        $('#music-forward-btn').addEventListener('click', () => player.audio.currentTime += 10);
        $('#music-rewind-btn').addEventListener('click', () => player.audio.currentTime -= 10);
        volumeSlider.addEventListener('input', (e) => setVolume(e.target.value));
 
        loopBtn.addEventListener('click', () => { player.loop = !player.loop; player.audio.loop = player.loop; loopBtn.classList.toggle('active', player.loop); });
        autoplayBtn.addEventListener('click', () => { player.autoplay = !player.autoplay; autoplayBtn.classList.toggle('active', player.autoplay); });
        
        player.audio.addEventListener('timeupdate', () => {
            const { currentTime, duration } = player.audio;
            if (duration) {
                progressBar.style.width = `${(currentTime / duration) * 100}%`;
                const formatTime = (s) => `${Math.floor(s/60)}:${String(Math.floor(s%60)).padStart(2,'0')}`;
                currentTimeEl.textContent = formatTime(currentTime);
                durationEl.textContent = formatTime(duration);
            }
        });
        player.audio.addEventListener('ended', () => { if (player.autoplay) { nextSong(); } else { pauseSong(); player.audio.currentTime = 0; } });
        progressContainer.addEventListener('click', (e) => { if (!isNaN(player.audio.duration)) { player.audio.currentTime = (e.offsetX / progressContainer.clientWidth) * player.audio.duration; } });
        $('#music-playlist-btn').addEventListener('click', () => menuEl.classList.add('visible'));
        $('#music-search-input').addEventListener('input', (e) => renderSongList(e.target.value.toLowerCase()));
        menuEl.addEventListener('click', (e) => { if(e.target.id === 'trix-music-menu') menuEl.classList.remove('visible'); });
 
        setVolume(player.volume);
        autoplayBtn.classList.toggle('active', player.autoplay);
        loadSong(player.currentSongIndex);
        renderSongList();
        return player;
    }
 
    function showLoadingScreen() {
        const themeVars = { accent: '#3182CE', shadow1: '#2B6CB0', shadow2: '#2C5282' };
        const style = document.createElement('style');
        style.textContent = `
            #trix-loading-screen, #trix-loading-settings-modal { font-family: 'Inter', sans-serif; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: #12141d; z-index: 2147483647; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #fff; opacity: 0; transition: opacity 0.5s ease-in-out; pointer-events: none; }
            #trix-loading-screen.visible, #trix-loading-settings-modal.visible { opacity: 1; pointer-events: auto; }
            .loading-content { opacity: 1; transition: opacity 1s ease-in-out; text-align: center; }
            .loading-content.fade-out { opacity: 0; }
            .loading-header { font-size: 4rem; font-weight: 700; color: ${themeVars.accent}; text-shadow: 2px 2px 15px rgba(49, 130, 206, 0.5); margin-bottom: 2rem; }
            .loading-bar-container { width: 50%; max-width: 600px; height: 16px; background-color: #1A202C; border-radius: 99px; overflow: hidden; margin-bottom: 1rem; }
            .loading-bar-progress { width: 0%; height: 100%; background: ${themeVars.accent}; border-radius: 99px; transition: width 0.1s linear; }
            .loading-info { display: flex; justify-content: center; gap: 20px; font-size: 1rem; margin-bottom: 2rem; color: #A0AEC0; }
            .loading-message { font-style: italic; color: #718096; min-height: 1.2em; }
            #trix-loading-settings-modal .settings-grid { display: grid; grid-template-columns: auto auto; gap: 1rem 2rem; align-items: center; margin-top: 2rem; }
            #trix-loading-settings-modal input[type="checkbox"] { width: 20px; height: 20px; accent-color: ${themeVars.accent}; }
            #trix-loading-settings-modal .trix-button { background-color: ${themeVars.accent}; color: white; border: none; padding: 10px 25px; cursor: pointer; border-radius: 6px; flex-grow: 0; margin-top: 2rem; transition: filter 0.2s; font-weight: 600; }
        `;
        document.documentElement.appendChild(style);
 
        const loadingScreen = document.createElement('div');
        loadingScreen.id = 'trix-loading-screen';
        const loadingMessages = [ "Disable other extensions to reduce disconnects.", "The worst it can do is crash your game, right?", "Definitely not a cheating tool...", "You can report harmful scripts on Discord: painsel", "Wanna break from the ads?", "If you're a developer, help is welcome! (Discord: painsel)" ];
        loadingScreen.innerHTML = `<div class="loading-content"><div class="loading-header">TriX Executor</div><div class="loading-bar-container"><div class="loading-bar-progress"></div></div><div class="loading-info"><span class="loading-percentage">0.0%</span><span class="loading-timer">...s left</span></div><div class="loading-message">${loadingMessages[0]}</div></div>`;
        
        const settingsModal = document.createElement('div');
        settingsModal.id = 'trix-loading-settings-modal';
        settingsModal.style.visibility = 'hidden';
        settingsModal.innerHTML = `<div class="loading-content"><div class="loading-header">Settings</div><div class="settings-grid"><label for="trix-antilag-check">Anti-Lag Mode:</label><input type="checkbox" id="trix-antilag-check"></div><button id="trix-resume-loading" class="trix-button">Resume</button></div>`;
 
        document.documentElement.appendChild(loadingScreen);
        document.documentElement.appendChild(settingsModal);
 
        const antiLagCheck = settingsModal.querySelector('#trix-antilag-check');
        antiLagCheck.checked = settings.antiLag;
        antiLagCheck.onchange = () => { settings.antiLag = antiLagCheck.checked; GM_setValue('trixSettings', settings); };
        
        setTimeout(() => loadingScreen.classList.add('visible'), 10);
 
        const progressBar = loadingScreen.querySelector('.loading-bar-progress'), percentageText = loadingScreen.querySelector('.loading-percentage'), timerText = loadingScreen.querySelector('.loading-timer'), messageText = loadingScreen.querySelector('.loading-message'), loadingContent = loadingScreen.querySelector('.loading-content');
        const randomDuration = Math.random() * (20000 - 8000) + 8000;
        let startTime = null, lastMessageChange = 0, currentMessageIndex = 0, isPaused = false, pauseTime = 0, animFrameId;
 
        function loadingLoop(timestamp) {
            if (!startTime) startTime = timestamp;
            if (isPaused) { animFrameId = requestAnimationFrame(loadingLoop); return; }
            const elapsed = timestamp - startTime, progress = Math.min(elapsed / randomDuration, 1);
            progressBar.style.width = `${progress * 100}%`;
            percentageText.textContent = `${(progress * 100).toFixed(1)}%`;
            timerText.textContent = `${Math.max(0, (randomDuration - elapsed) / 1000).toFixed(1)}s left`;
            if (elapsed > lastMessageChange + 4000) {
                lastMessageChange = elapsed;
                let newIndex; do { newIndex = Math.floor(Math.random() * loadingMessages.length); } while (newIndex === currentMessageIndex);
                currentMessageIndex = newIndex; messageText.textContent = loadingMessages[currentMessageIndex];
            }
            if (progress < 1) { animFrameId = requestAnimationFrame(loadingLoop); } else {
                window.removeEventListener('keydown', f12Handler);
                setTimeout(() => {
                    loadingContent.classList.add('fade-out');
                    setTimeout(() => {
                        loadingScreen.classList.remove('visible');
                        settingsModal.classList.remove('visible');
                        setTimeout(() => { loadingScreen.remove(); settingsModal.remove(); style.remove(); }, 1500);
                    }, 1000);
                }, 500);
            }
        }
 
        const f12Handler = (e) => {
            if (e.key === 'F12') { e.preventDefault(); isPaused = true; pauseTime = performance.now(); settingsModal.style.visibility = 'visible'; settingsModal.classList.add('visible'); }
        };
 
        settingsModal.querySelector('#trix-resume-loading').onclick = () => {
            settingsModal.classList.remove('visible');
            setTimeout(() => settingsModal.style.visibility = 'hidden', 500);
            startTime += (performance.now() - pauseTime);
            isPaused = false;
        };
 
        window.addEventListener('keydown', f12Handler);
        animFrameId = requestAnimationFrame(loadingLoop);
    }
 
    function run() {
        let loadCount = GM_getValue('trixLoadCount', 0);
        GM_setValue('trixLoadCount', loadCount + 1);
        if ((loadCount + 1) % 5 === 1) { showLoadingScreen(); }
        waitForElement('#canvasA', initialize);
    }
 
    function waitForElement(selector, callback) {
        if (document.querySelector(selector)) { return callback(); }
        const observer = new MutationObserver(() => { if (document.querySelector(selector)) { observer.disconnect(); callback(); } });
        observer.observe(document.documentElement, { childList: true, subtree: true });
    }
 
    if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', run); } else { run(); }
 
})();