TriX Executor (BETA) for Territorial.io

A powerful, multi-tabbed, persistent code execution and network logging environment for developers. Features a refined UI, customizable themes, 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-Aether-2024.06.12
// @description  A powerful, multi-tabbed, persistent code execution and network logging environment for developers. Features a refined UI, customizable themes, 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
// @require      https://cdn.jsdelivr.net/npm/@emotion/css@11/dist/emotion-css.umd.min.js
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        unsafeWindow
// @run-at       document-start
// @license      MIT
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzAwYWFmZiI+PHBhdGggZD0iTTE4LjM2IDIyLjA1bC00LjQyLTQuNDJDMTMuNDcgMTcuODcgMTMgMTguMTcgMTMgMTguNUMxMyAyMC45OSAxNS4wMSAyMyAxNy41IDIzaDMuNTRjLTIuNDUtMS40OC00LjQyLTMuNDUtNS5LTUuOTV6TTggMTNjMS42NiAwIDMuMTgtLjU5IDQuMzgtMS42MkwxMCAxMy41VjIybDIuNS0yLjVMMTggMTMuOTZjLjM1LS40OC42NS0xIC44Ny0xLjU1QzE4LjYxIDEzLjQxIDE4IDEyLjM0IDE4IDEyVjBoLTJ2MTJjMCAuMzQtLjAyLjY3LS4wNiAxLS4zMy4xOC0uNjguMy0xLjA0LjM3LTEuNzMgMC0zLjI3LS45My00LjE2LTIuMzZMNiAxMy4sixVjVINXY4eiIvPjwvc3ZnPg==
// ==/UserScript==

/* global Prism, unsafeWindow, emotion */

(function() {
    'use strict';
    const { css, keyframes } = emotion;

    // --- 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: 'abyss', // abyss, terminal_green, crimson_night
        antiLag: false,
        gameLogToasts: true
    };
    let interceptionEnabled = false;
    let isLoggerSuspended = false;
    let queuedMessages = new Map();
    let renderInterceptorQueue = () => {};
    let customWs = 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 };

    // --- UI Theming & Styling (with Emotion) ---
    const themes = {
        abyss: {
            accent: '#0095FF', accentHover: '#33ADFF', accentTranslucent: 'rgba(0, 149, 255, 0.2)',
            text: '#E0E0E0', textMuted: '#A0A0A0', textHeader: '#FFFFFF',
            bgMain: '#1A1D24', bgHeader: '#242831', bgContent: '#20232A',
            bgMainTranslucent: 'rgba(26, 29, 36, 0.85)',
            border: '#3A3F4C',
            shadow: 'rgba(0, 0, 0, 0.3)',
            fontBody: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",
            fontMono: "'Fira Code', 'Consolas', monospace",
            borderRadius: '8px',
            specialTextShadow: 'none',
            specialContainerFilter: 'none',
        },
        terminal_green: {
            accent: '#00FF9B', accentHover: '#3AFFB2', accentTranslucent: 'rgba(0, 255, 155, 0.2)',
            text: '#00FF9B', textMuted: '#00b36d', textHeader: '#3AFFB2',
            bgMain: '#0D0D0D', bgHeader: '#121212', bgContent: '#101010',
            bgMainTranslucent: 'rgba(13, 13, 13, 0.8)',
            border: '#00FF9B',
            shadow: '#00b36d',
            fontBody: "'Courier New', monospace",
            fontMono: "'Courier New', monospace",
            borderRadius: '0px',
            specialTextShadow: '0 0 5px #00FF9B, 0 0 2px #00FF9B',
            specialContainerFilter: 'url(#scanlines)',
        },
        crimson_night: {
            accent: '#E11D48', accentHover: '#F43F5E', accentTranslucent: 'rgba(225, 29, 72, 0.2)',
            text: '#FCE7F3', textMuted: '#FDA4AF', textHeader: '#FFFFFF',
            bgMain: '#1F0A0E', bgHeader: '#2c0e14', bgContent: '#250c12',
            bgMainTranslucent: 'rgba(31, 10, 14, 0.85)',
            border: '#881337',
            shadow: 'rgba(0, 0, 0, 0.5)',
            fontBody: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",
            fontMono: "'Fira Code', 'Consolas', monospace",
            borderRadius: '8px',
            specialTextShadow: '1px 1px 2px rgba(0,0,0,0.5)',
            specialContainerFilter: 'url(#noise)',
        }
    };

    function createStyles() {
        const styleElement = document.createElement('style');

        // Define keyframes using Emotion
        const slideIn = keyframes`from { transform: translateX(100%) scale(0.98); opacity: 0; } to { transform: translateX(0) scale(1); opacity: 1; }`;
        const fadeIn = keyframes`from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); }`;
        const toastIn = keyframes`from { transform: translateX(110%); opacity: 0; } to { transform: translateX(0); opacity: 1; }`;
        const toastOut = keyframes`from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.9); }`;
        const rainbowGlow = keyframes`0%{box-shadow:0 0 10px 2px hsl(0,100%,60%)} 16%{box-shadow:0 0 10px 2px hsl(60,100%,60%)} 33%{box-shadow:0 0 10px 2px hsl(120,100%,60%)} 50%{box-shadow:0 0 10px 2px hsl(180,100%,60%)} 66%{box-shadow:0 0 10px 2px hsl(240,100%,60%)} 83%{box-shadow:0 0 10px 2px hsl(300,100%,60%)} 100%{box-shadow:0 0 10px 2px hsl(360,100%,60%)}`;

        // Generate theme variables
        let themeStyles = '';
        for (const themeName in themes) {
            themeStyles += `
                :host([data-theme="${themeName}"]) {
                    ${Object.entries(themes[themeName]).map(([key, value]) => `--${key.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${value};`).join('\n')}
                }
            `;
        }

        // Use Emotion's `css` helper to generate the main stylesheet
        styleElement.textContent = `
            /* --- PrismJS Theme (3rd Party) --- */
            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;tab-size:4;}pre[class*="language-"]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*="language-"],pre[class*="language-"]{background:#2d2d2d}:not(pre)>code[class*="language-"]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment{color:#999}.token.punctuation{color:#ccc}.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc}

            /* --- Base & Theme Variables --- */
            :host {
                ${Object.entries(themes.abyss).map(([key, value]) => `--${key.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${value};`).join('\n')}
            }
            ${themeStyles}

            /* --- Main UI Elements --- */
            #trix-toggle-btn { position: fixed; top: 15px; right: 15px; z-index: 99999; width: 50px; height: 50px; background-color: var(--bg-main-translucent); color: var(--accent); border: 2px solid var(--accent); border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 24px; transition: all 0.3s ease; backdrop-filter: blur(8px); font-family: var(--font-mono); box-shadow: 0 0 15px var(--accent-translucent); }
            #trix-toggle-btn:hover { transform: scale(1.1) rotate(15deg); box-shadow: 0 0 20px var(--accent); }

            #trix-container { position: fixed; top: 80px; right: 15px; width: 500px; min-height: 450px; z-index: 99998; color: var(--text); font-family: var(--font-body); border-radius: var(--border-radius); overflow: hidden; box-shadow: 0 8px 32px var(--shadow); display: flex; flex-direction: column; backdrop-filter: blur(12px); animation: ${slideIn} 0.5s cubic-bezier(0.25, 1, 0.5, 1); resize: both; background-color: var(--bg-main-translucent); border: 1px solid var(--border); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); }
            #trix-container::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: -1; filter: var(--special-container-filter); pointer-events: none; }
            #trix-container.hidden { display: none; }
            #trix-header, #trix-footer { cursor: move; user-select: none; flex-shrink: 0; }
            #trix-header { padding: 10px 15px; display: flex; justify-content: space-between; align-items: center; background-color: var(--bg-header); border-bottom: 1px solid var(--border); }
            .trix-title { font-size: 16px; font-weight: 600; color: var(--text-header); text-shadow: var(--special-text-shadow); }
            .trix-version-tag { font-size: 10px; font-weight: 300; opacity: 0.7; margin-left: 8px; }
            #trix-header-controls { display: flex; align-items: center; gap: 15px; font-size: 12px; }
            #trix-close-btn { cursor: pointer; font-size: 20px; font-weight: bold; opacity: 0.7; transition: all 0.2s; }
            #trix-close-btn:hover { opacity: 1; color: #ef4444; transform: scale(1.1); }
            #trix-conn-status { width: 10px; height: 10px; border-radius: 50%; }
            #trix-conn-status.connected { background-color: #28a745; } #trix-conn-status.disconnected { background-color: #dc3545; } #trix-conn-status.connecting { background-color: #ffc107; }
            .ping-good { color: #28a745; } .ping-ok { color: #ffc107; } .ping-bad { color: #dc3545; }

            /* --- Tabs & Content --- */
            #trix-content { padding: 0 15px 15px; flex-grow: 1; display: flex; flex-direction: column; overflow: hidden; background-color: var(--bg-content); }
            .trix-tabs { display: flex; flex-wrap: wrap; flex-shrink: 0; border-bottom: 1px solid var(--border); }
            .trix-tab { background: transparent; color: var(--text-muted); padding: 10px 15px; cursor: pointer; border-bottom: 2px solid transparent; position: relative; transition: all 0.2s ease; }
            .trix-tab:hover { background: var(--bg-header); color: var(--text); }
            .trix-tab.active { font-weight: 600; color: var(--text-header); border-bottom-color: var(--accent); background: var(--bg-content); }
            .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: #ef4444; }
            #trix-new-tab-btn { background: none; border: none; color: var(--text-muted); font-size: 24px; cursor: pointer; padding: 5px 10px; transition: all 0.2s; }
            #trix-new-tab-btn:hover { color: var(--accent); transform: scale(1.1); }
            .trix-tab-rename-input { background: transparent; border: 1px solid var(--accent); color: inherit; font-family: inherit; font-size: inherit; padding: 2px; margin: 0; width: 100px; }
            .trix-view { display: flex; flex-direction: column; flex-grow: 1; padding-top: 15px; overflow: hidden; }

            /* --- Controls & Inputs --- */
            .trix-action-bar { display: flex; gap: 10px; margin-top: 10px; flex-shrink: 0; }
            .trix-button { background-color: var(--accent); color: white; border: none; padding: 10px 15px; cursor: pointer; border-radius: 6px; flex-grow: 1; transition: all 0.2s; font-weight: 600; text-shadow: 1px 1px 2px rgba(0,0,0,0.2); background-image: linear-gradient(to top, rgba(0,0,0,0.1), transparent); }
            .trix-button:hover { filter: brightness(1.1); background-color: var(--accent-hover); transform: translateY(-1px); box-shadow: 0 4px 8px rgba(0,0,0,0.2); }
            .trix-button:active { transform: translateY(0); box-shadow: none; }
            .trix-button:disabled { background-color: #555; cursor: not-allowed; filter: none; box-shadow: none; }
            .trix-input, select.trix-input { background: var(--bg-main); border: 1px solid var(--border); color: var(--text); padding: 8px; border-radius: 5px; font-family: var(--font-mono); box-sizing: border-box; transition: all 0.2s; }
            .trix-input:focus, select.trix-input:focus { border-color: var(--accent); outline: none; box-shadow: 0 0 0 3px var(--accent-translucent); }
            .trix-status-bar { margin-top: 10px; padding: 8px; background: rgba(0,0,0,0.2); font-size: 12px; border-radius: 4px; min-height: 1.2em; }

            /* --- Specific Views --- */
            #trix-welcome-view h2 { margin-top: 0; color: var(--text-header); }
            #trix-welcome-view p { color: var(--text); line-height: 1.6; }
            #trix-welcome-view code { background: var(--bg-main); padding: 2px 5px; border-radius: 4px; font-family: var(--font-mono); }
            #trix-ws-client-view, #trix-packet-log-view, #trix-interceptor-view, #trix-storage-view-container, #trix-script-injector-container { gap: 15px; }
            #trix-ws-console, #trix-packet-log, #trix-interceptor-queue { flex-grow: 1; background: var(--bg-main); border: 1px solid var(--border); border-radius: 6px; padding: 10px; overflow-y: auto; font-family: var(--font-mono); font-size: 12px; }
            .ws-console-send { color: #7ec699; } .ws-console-receive { color: #cc99cd; } .ws-console-system { color: #f08d49; }
            #trix-toggle-interception-btn.active { background-color: #dc3545; box-shadow: 0 0 8px #dc3545; }

            /* --- Music Player --- */
            #trix-music-toggle-btn { position: fixed; bottom: 20px; left: 20px; z-index: 99999; width: 50px; height: 50px; background-color: var(--bg-main-translucent); color: var(--text); border: 1px solid var(--border); border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(8px); transition: all 0.3s ease; }
            #trix-music-toggle-btn:hover { transform: scale(1.1); box-shadow: 0 0 10px var(--accent); color: var(--accent); }
            #trix-music-player { display: none; flex-direction: column; position: fixed; bottom: 80px; left: 20px; width: 300px; background: var(--bg-main-translucent); border: 1px solid var(--border); backdrop-filter: blur(12px); border-radius: var(--border-radius); padding: 15px; z-index: 99998; box-shadow: 0 8px 32px var(--shadow); opacity: 0; transform: scale(0.95) translateY(10px); transition: opacity 0.3s ease, transform 0.3s ease; }
            #trix-music-player.visible { display: flex; opacity: 1; transform: scale(1) translateY(0); }
            #trix-music-player.rainbow-outline { animation: ${rainbowGlow} 2s linear infinite; border-color: transparent; }
            .music-title { font-weight: 600; text-shadow: var(--special-text-shadow); }
            .music-progress-container { background: var(--bg-content); border-radius: 5px; cursor: pointer; height: 6px; }
            .music-progress-bar { background: var(--accent); width: 0%; height: 100%; border-radius: 5px; }

            /* --- Modal & Menu --- */
            #trix-music-menu, #trix-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 100000; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(5px); opacity: 0; transition: opacity 0.3s; pointer-events: none; animation: ${fadeIn} 0.3s ease forwards; }
            #trix-music-menu.visible, #trix-modal.visible { opacity: 1; pointer-events: auto; }
            .music-menu-content, .modal-content { background: var(--bg-content); border: 1px solid var(--border); width: 90%; max-width: 500px; max-height: 80%; border-radius: var(--border-radius); display: flex; flex-direction: column; box-shadow: 0 8px 32px var(--shadow); }

            /* --- Toast Notifications --- */
            #trix-toast-container { position: fixed; bottom: 20px; right: 20px; z-index: 100001; display: flex; flex-direction: column; gap: 10px; align-items: flex-end; }
            .trix-toast { background-color: var(--bg-header); color: var(--text); border-left: 5px solid var(--accent); padding: 15px; border-radius: var(--border-radius); box-shadow: 0 8px 32px var(--shadow); width: 350px; animation: ${toastIn} 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); position: relative; overflow: hidden; cursor: pointer; border: 1px solid var(--border); }
            .trix-toast.fade-out { animation: ${toastOut} 0.4s ease-in forwards; }
            .toast-body code { background: var(--bg-content); padding: 2px 4px; border-radius: 4px; }
            .toast-progress { background-color: var(--accent); }
        `;
        return styleElement;
    }

    // --- 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.');
        });
        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';
        shadowHost.dataset.theme = settings.theme;
        document.body.appendChild(shadowHost);
        const shadowRoot = shadowHost.attachShadow({ mode: 'open' });

        const styleElement = createStyles();
        shadowRoot.appendChild(styleElement);

        // Add SVG filters for special theme effects
        const svgFilters = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        svgFilters.style.position = 'absolute';
        svgFilters.style.width = '0';
        svgFilters.style.height = '0';
        svgFilters.innerHTML = `
            <defs>
                <filter id="scanlines">
                    <feImage href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQYV2NgYGD4z/Cf6WcABgAHiQEtA8s37wAAAABJRU5ErkJggg==" x="0" y="0" width="1" height="2" />
                    <feTile result="pattern"/>
                    <feComposite in="pattern" in2="SourceAlpha" operator="in"/>
                    <feColorMatrix type="matrix" values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.2 0" />
                </filter>
                <filter id="noise">
                    <feTurbulence type="fractalNoise" baseFrequency="0.65" numOctaves="3" stitchTiles="stitch"/>
                    <feColorMatrix type="matrix" values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.1 0" />
                </filter>
            </defs>
        `;
        shadowRoot.appendChild(svgFilters);


        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" style="position: absolute; top: 0; left: 0; width: 100%; height: 20px; cursor: move;"></div>
                <div class="music-info" style="text-align: center; margin-bottom: 10px;"><span class="music-title">Select a Song</span><span class="music-artist" style="font-size: 12px; color: var(--text-muted); display: block;">...</span></div>
                <div class="music-progress-container"><div class="music-progress-bar"></div></div>
                <div class="music-time" style="display: flex; justify-content: space-between; font-size: 11px; color: var(--text-muted);"><span id="music-current-time">0:00</span><span id="music-duration">0:00</span></div>
                <div class="music-controls" style="display: flex; justify-content: space-around; align-items: center; margin: 10px 0;">
                    <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" style="display: flex; justify-content: space-around; align-items: center; width: 100%; margin-bottom: 10px;">
                    <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" style="display: flex; align-items: center; justify-content: center; position: relative;"><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 };
        }
        // ... (The rest of the JS logic remains largely the same)
        // Only functions that directly manipulate HTML or styles need to be aware of the new class names if they changed.
        // But since this was a style-only overhaul, most of the logic can stay untouched. The original functions for
        // rendering tabs, handling clicks, etc., will work as before.

        // Minor adjustments might be needed if class names were changed for styling purposes.
        // For example, if .trix-button was changed to .trix-btn, all JS querySelectors would need updating.
        // In this refined version, the class names are kept consistent to minimize logical changes.

        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: 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" style="background-color:#0d6efd;">Save</button>
                                <button id="trix-ws-del-msg-btn" class="trix-button" style="background-color:#6c757d;">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" style="background-color:#28a745;">Forward All</button>
                            <button id="trix-block-all-btn" class="trix-button" style="background-color:#dc3545;">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 class="trix-injector-grid" style="flex-grow:1; display:flex; flex-direction:column;">
                            <textarea id="trix-injector-message" class="trix-input" 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" style="background-color:#0d6efd;">Save</button>
                                    <button id="trix-injector-del-msg-btn" class="trix-button" style="background-color:#6c757d;">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">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">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>${msg.data}</textarea>
                    <div class="interceptor-item-actions">
                        <button class="trix-button interceptor-btn interceptor-forward-btn">Forward</button>
                        <button class="trix-button interceptor-btn interceptor-block-btn">Block</button>
                    </div>`;
                queueContainer.appendChild(item);
            }
        };

        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('connected'); 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('connected');
                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('connected'); 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'), $('#trix-footer')]);
            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('interceptor-forward-btn')) {
                    const editedData = messageItem.querySelector('textarea').value;
                    message.resolve({ action: 'forward', data: editedData });
                } else if (target.classList.contains('interceptor-block-btn')) {
                    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">Suspend Log</button><button id="trix-clear-log-btn" class="trix-button">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' ? '#28a745' : '#ffc107';
                    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';

            const duration = options.duration || (Math.random() * (16000 - 8000) + 8000);
            const startTime = performance.now();

            let actionsHTML = '';
            if (options.action) {
                actionsHTML = `<div class="toast-actions"><button class="toast-button">${options.action.text}</button></div>`;
            }

            toast.innerHTML = `<div class="toast-header"><span>${title}</span><span class="toast-timer">${(duration/1000).toFixed(1)}s</span></div><div class="toast-body">${body}</div>${actionsHTML}<div class="toast-progress"></div>`;
            toastContainer.appendChild(toast);

            const timerEl = toast.querySelector('.toast-timer');
            const progressEl = toast.querySelector('.toast-progress');

            const intervalId = setInterval(() => {
                const elapsed = performance.now() - startTime;
                const remaining = duration - elapsed;
                if (remaining <= 0) { clearInterval(intervalId); return; }
                timerEl.textContent = `${(remaining / 1000).toFixed(1)}s`;
                progressEl.style.width = `${(remaining / duration) * 100}%`;
            }, 100);

            const dismissToast = () => {
                clearInterval(intervalId);
                clearTimeout(timeoutId);
                toast.classList.add('fade-out');
                toast.addEventListener('animationend', () => toast.remove(), { once: true });
            };

            if (options.action) {
                toast.querySelector('.toast-button').addEventListener('click', () => { options.action.callback(); dismissToast(); });
            }

            const timeoutId = setTimeout(dismissToast, duration);
            toast.addEventListener('click', (e) => { if (!e.target.classList.contains('toast-button')) 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);

        let gameLogObserver;
        let logMessageQueue = [];
        let logToastTimer = null;
        function processAndShowLogToast() {
            if (logMessageQueue.length === 0) return;
            const toastBody = logMessageQueue.map(item => `<div class="toast-log-entry"><span class="toast-log-timestamp">${item.timestamp}</span><span>${item.message}</span></div>`).join('');
            showToast('Game Log Update', toastBody, { duration: 8000 });
            logMessageQueue = [];
        }

        function observeGameLog() {
            if (gameLogObserver) gameLogObserver.disconnect();
            const gameLogContainer = document.querySelector("body > div:nth-child(4) > div:nth-child(1)");
            if (gameLogContainer) {
                gameLogObserver = new MutationObserver((mutations) => {
                    if (settings.antiLag || !settings.gameLogToasts) return;
                    let hasNewNodes = false;
                    for (const mutation of mutations) {
                        if (mutation.type === 'childList') {
                            mutation.addedNodes.forEach(node => {
                                if (node.nodeType === Node.ELEMENT_NODE && node.style.display === 'table') {
                                    const timestampNode = node.querySelector('span:first-child');
                                    const messageNode = node.querySelector('span:last-child');
                                    if (timestampNode && messageNode) {
                                        logMessageQueue.push({ timestamp: timestampNode.textContent, message: messageNode.innerHTML });
                                        hasNewNodes = true;
                                    }
                                }
                            });
                        }
                    }
                    if (hasNewNodes) {
                        clearTimeout(logToastTimer);
                        logToastTimer = setTimeout(processAndShowLogToast, 1500);
                    }
                });
                gameLogObserver.observe(gameLogContainer, { childList: true, subtree: true });
            } else {
                setTimeout(observeGameLog, 1000);
            }
        }
        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;

        const updateSpecialEffects = () => 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();
            updateSpecialEffects();
        };
        const playSong = () => { player.isPlaying = true; player.audio.play().catch(console.error); playPauseBtn.innerHTML = `<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>`; updateSpecialEffects(); };
        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>`; updateSpecialEffects(); };
        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(s => s.title.toLowerCase().includes(filter) || s.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 currentTheme = themes[settings.theme] || themes.abyss;

        const style = document.createElement('style');
        style.textContent = `
            #trix-loading-screen, #trix-loading-settings-modal { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: #000; z-index: 2147483647; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #fff; font-family: ${currentTheme.fontBody}; 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: 5rem; font-weight: bold; color: ${currentTheme.accent}; text-shadow: 2px 2px 0px ${currentTheme.accentHover}, 4px 4px 10px rgba(0,0,0,0.5); margin-bottom: 2rem; }
            .loading-bar-container { width: 50%; max-width: 600px; height: 20px; background-color: #2a2a30; border-radius: 10px; border: 1px solid ${currentTheme.border}; overflow: hidden; margin-bottom: 1rem; }
            .loading-bar-progress { width: 0%; height: 100%; background: linear-gradient(90deg, ${currentTheme.accent}, ${currentTheme.accentHover}); border-radius: 10px; transition: width 0.1s linear; }
            .loading-info { display: flex; justify-content: center; gap: 20px; font-size: 1.2rem; margin-bottom: 2rem; }
            .loading-message { font-style: italic; color: #9ca3af; min-height: 1.2em; }
            #trix-loading-settings-modal .settings-grid { display: grid; grid-template-columns: 1fr 2fr; gap: 1rem 2rem; align-items: center; margin-top: 2rem; }
            #trix-loading-settings-modal .settings-label { text-align: right; font-weight: bold; }
            #trix-loading-settings-modal select, #trix-loading-settings-modal input[type="checkbox"] { background: #2a2a30; color: white; border: 1px solid #444; padding: 5px; border-radius: 4px; }
            #trix-loading-settings-modal input[type="checkbox"] { width: 20px; height: 20px; }
            #trix-loading-settings-modal .trix-button { background-color: ${currentTheme.accent}; color: white; border: none; padding: 10px 15px; cursor: pointer; border-radius: 6px; flex-grow: 0; width: 200px; margin-top: 2rem; transition: filter 0.2s; font-weight: 600; }
            #trix-loading-settings-modal .trix-button:hover { filter: brightness(1.1); }
        `;
        document.documentElement.appendChild(style);

        const loadingScreen = document.createElement('div');
        loadingScreen.id = 'trix-loading-screen';
        const loadingMessages = [ "Disable all other extensions to reduce disconnections (Error 1009).", "the worst it can do is crash your game, right?", "Definitely not a cheating tool...", "did you know? I didnt know", "you can report any harmful scripts to me. (my discord: painsel)", "wanna break from the ads?", "I'm not sure how some features even work, if you're a developer pls help me (my 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 class="settings-label" for="trix-theme-select">Theme:</label>
                    <select id="trix-theme-select">
                        <option value="abyss">Abyss (Default)</option>
                        <option value="terminal_green">Terminal Green</option>
                        <option value="crimson_night">Crimson Night</option>
                    </select>
                    <label class="settings-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.append(loadingScreen, settingsModal);

        const themeSelect = settingsModal.querySelector('#trix-theme-select');
        const antiLagCheck = settingsModal.querySelector('#trix-antilag-check');
        themeSelect.value = settings.theme;
        antiLagCheck.checked = settings.antiLag;

        const saveSettings = () => GM_setValue('trixSettings', settings);
        themeSelect.onchange = () => { settings.theme = themeSelect.value; saveSettings(); };
        antiLagCheck.onchange = () => { settings.antiLag = antiLagCheck.checked; saveSettings(); };

        setTimeout(() => loadingScreen.classList.add('visible'), 10);

        const progressBar = loadingScreen.querySelector('.loading-bar-progress');
        const percentageText = loadingScreen.querySelector('.loading-percentage');
        const timerText = loadingScreen.querySelector('.loading-timer');
        const messageText = loadingScreen.querySelector('.loading-message');
        const loadingContent = loadingScreen.querySelector('.loading-content');

        const randomDuration = Math.random() * (5000 - 2000) + 2000;
        let startTime = null;
        let lastMessageChange = 0;
        let currentMessageIndex = 0;
        let isPaused = false;
        let pauseTime = 0;
        let animFrameId;

        function loadingLoop(timestamp) {
            if (!startTime) startTime = timestamp;
            if (isPaused) { animFrameId = requestAnimationFrame(loadingLoop); return; }

            const elapsed = timestamp - startTime;
            const 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 + 1500 && progress < 0.9) {
                lastMessageChange = elapsed;
                currentMessageIndex = (currentMessageIndex + 1) % loadingMessages.length;
                messageText.textContent = loadingMessages[currentMessageIndex];
            }

            if (progress < 1) {
                animFrameId = requestAnimationFrame(loadingLoop);
            } else {
                window.removeEventListener('keydown', f12Handler);
                percentageText.textContent = '100.0%';
                timerText.textContent = '0.0s left';
                setTimeout(() => {
                    loadingContent.classList.add('fade-out');
                    setTimeout(() => {
                        loadingScreen.classList.remove('visible');
                        settingsModal.classList.remove('visible');
                        setTimeout(() => { loadingScreen.remove(); settingsModal.remove(); style.remove(); }, 1000);
                    }, 500);
                }, 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) {
        const el = document.querySelector(selector);
        if (el) { callback(); return; }
        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();
    }

})();