TriX Executor (BETA) for Territorial.io

BEST EXPLOIT IN GAME1! REMINDER: it is still in a very early stage. report any bugs or issues to the author and please dont judge too harshly at this stage. Join TriX Discord server to receive more updates: https://discord.gg/Zp7cqqZzXX have fun cheating :D

目前為 2025-09-13 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         TriX Executor (BETA) for Territorial.io
// @namespace    https://greasyfork.org/en/users/COURTESYCOIL
// @version      Beta-Andromeda-2024.08.20
// @description  BEST EXPLOIT IN GAME1! REMINDER: it is still in a very early stage. report any bugs or issues to the author and please dont judge too harshly at this stage. Join TriX Discord server to receive more updates: https://discord.gg/Zp7cqqZzXX have fun cheating :D
// @author       Painsel
// @match        *://territorial.io/*
// @require      https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        unsafeWindow
// @run-at       document-start
// @license      MIT
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzAwYWFmZiI+PHBhdGggZD0iTTE4LjM2IDIyLjA1bC00LjQyLTQuNDJDMTMuNDcgMTcuODcgMTMgMTguMTcgMTMgMTguNUMxMyAyMC45OSAxNS4wMSAyMyAxNy41IDIzaDMuNTRjLTIuNDUtMS40OC00LjQyLTMuNDUtNS5LTUuOTV6TTggMTNjMS42NiAwIDMuMTgtLjU5IDQuMzgtMS42MkwxMCAxMy41VjIybDIuNS0yLjVMMTggMTMuOTZjLjM1LS40OC42NS0xIC44Ny0xLjU1QzE4LjYxIDEzLjQxIDE4IDEyLjM0IDE4IDEyVjBoLTJ2MTJjMCAuMzQtLjAyLjY3LS4wNiAxLS4zMy4xOC0uNjguMy0xLjA0LjM3LTEuNzMgMC0zLjI3LS45My00LjE2LTIuMzZMNiAxMy42VjVINXY4eiIvPjwvc3ZnPg==
// ==/UserScript==

/* global Prism, unsafeWindow */

(function() {
    'use strict';

    // --- Global State for Advanced Features ---
    let interceptionEnabled = false;
    let queuedMessages = new Map();
    let renderInterceptorQueue = () => {};
    let customWs = null;

    let updateConnectionStatus = () => {};
    let updatePingDisplay = () => {};
    let logPacketCallback = () => {};

    // --- WebSocket Proxy Setup ---
    const OriginalWebSocket = unsafeWindow.WebSocket;
    unsafeWindow.WebSocket = function(url, protocols) {
        if (!url.includes('/s52/')) { return new OriginalWebSocket(url, protocols); }
        console.log(`[TriX] Intercepting WebSocket connection to: ${url}`);
        const ws = new OriginalWebSocket(url, protocols);
        let originalOnMessageHandler = null;
        const originalSend = ws.send.bind(ws);
        ws.send = function(data) {
            if (interceptionEnabled) {
                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('send', `[Forwarded] ${decision.data}`);
                    } else { logPacketCallback('send', `[Blocked] ${data}`); }
                });
            } else { logPacketCallback('send', data); return originalSend(data); }
        };
        Object.defineProperty(ws, 'onmessage', {
            get: () => originalOnMessageHandler,
            set: (handler) => {
                originalOnMessageHandler = handler;
                ws.addEventListener('message', event => {
                    if (interceptionEnabled) {
                        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('receive', `[Forwarded] ${decision.data}`);
                                if (originalOnMessageHandler) originalOnMessageHandler({ data: decision.data });
                            } else { logPacketCallback('receive', `[Blocked] ${event.data}`); }
                        });
                    } else { logPacketCallback('receive', event.data); if (originalOnMessageHandler) originalOnMessageHandler(event); }
                });
            },
            configurable: true
        });
        ws.addEventListener('open', () => updateConnectionStatus('connected', 'Connection established.'));
        ws.addEventListener('close', (event) => {
            if (unsafeWindow.webSocketManager && unsafeWindow.webSocketManager.activeSocket === ws) unsafeWindow.webSocketManager.activeSocket = null;
            updateConnectionStatus('disconnected', `Disconnected. Code: ${event.code}`);
            updatePingDisplay('---');
        });
        ws.addEventListener('error', () => updateConnectionStatus('error', 'A connection error occurred.'));
        unsafeWindow.webSocketManager = { activeSocket: ws };
        return ws;
    };


    // --- Main script logic ---
    function initialize() {
        const shadowHost = document.createElement('div');
        shadowHost.id = 'trix-host';
        document.body.appendChild(shadowHost);
        const shadowRoot = shadowHost.attachShadow({ mode: 'open' });

        const styleElement = document.createElement('style');
        styleElement.textContent = `
            /* PrismJS Theme */
            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}
            
            /* --- Refined UI Theme --- */
            :host { --accent-color: #3b82f6; --bg-darker: #1e1e22; --bg-dark: #2a2a30; --border-color: #444; }
            @keyframes trix-fade-in { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
            @keyframes trix-slide-in { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
            
            #trix-toggle-btn { position: fixed; top: 15px; right: 15px; z-index: 99999; width: 50px; height: 50px; background-color: rgba(30, 30, 34, 0.8); color: #00aaff; border: 2px solid #00aaff; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 24px; transition: all 0.3s ease; backdrop-filter: blur(5px); font-family: monospace; box-shadow: 0 0 10px rgba(0, 170, 255, 0.5); }
            #trix-toggle-btn:hover { transform: scale(1.1) rotate(15deg); box-shadow: 0 0 15px #00aaff; }

            #trix-container { position: fixed; top: 80px; right: 15px; width: 500px; min-height: 450px; z-index: 99998; color: #e5e7eb; font-family: 'Segoe UI', 'Roboto', sans-serif; border-radius: 10px; overflow: hidden; box-shadow: 0 5px 25px rgba(0,0,0,0.5); display: flex; flex-direction: column; backdrop-filter: blur(10px); animation: trix-slide-in 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; resize: both; background-color: rgba(30, 30, 34, 0.9); border: 1px solid var(--border-color); }
            #trix-container.hidden { display: none; }
            #trix-header, #trix-footer { cursor: move; user-select: none; flex-shrink: 0; }
            #trix-header { padding: 10px 15px; display: flex; justify-content: space-between; align-items: center; background-color: rgba(0,0,0,0.3); }
            .trix-title { font-size: 16px; font-weight: 600; }
            .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; }
            #trix-close-btn:hover { opacity: 1; color: #ef4444; }

            #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; }

            #trix-content { padding: 0 15px 15px 15px; flex-grow: 1; display: flex; flex-direction: column; overflow: hidden; background-color: var(--bg-darker); }
            .trix-tabs { display: flex; flex-wrap: wrap; flex-shrink: 0; }
            .trix-tab { background: var(--bg-dark); color: #9ca3af; padding: 8px 12px; cursor: pointer; border-top: 2px solid transparent; border-left: 1px solid transparent; border-right: 1px solid transparent; border-radius: 5px 5px 0 0; margin-right: 2px; position: relative; }
            .trix-tab.active { background: var(--bg-darker); font-weight: 600; color: #e5e7eb; border-top-color: var(--accent-color); }
            .trix-tab-name { padding-right: 15px; } .trix-tab-close { position: absolute; top: 50%; right: 5px; transform: translateY(-50%); opacity: 0.6; } .trix-tab-close:hover { opacity: 1; color: #ef4444; }
            #trix-new-tab-btn { background: none; border: none; color: #9ca3af; font-size: 20px; cursor: pointer; padding: 5px 10px; }
            .trix-tab-rename-input { background: transparent; border: 1px solid var(--accent-color); color: inherit; font-family: inherit; font-size: inherit; padding: 0; margin: 0; width: 100px; }

            .trix-view { display: flex; flex-direction: column; flex-grow: 1; padding-top: 10px; overflow: hidden; }
            .trix-action-bar { display: flex; gap: 10px; margin-top: 10px; flex-shrink: 0; }
            .trix-button { background-color: var(--accent-color); color: white; border: none; padding: 10px 15px; cursor: pointer; border-radius: 6px; flex-grow: 1; transition: filter 0.2s; font-weight: 600; }
            .trix-button:hover { filter: brightness(1.1); }
            .trix-button:disabled { background-color: #555; cursor: not-allowed; filter: none; }
            .trix-input { background: #2d2d2d; border: 1px solid var(--border-color); color: #ccc; padding: 8px; border-radius: 5px; font-family: monospace; box-sizing: border-box; }
            .trix-status-bar { margin-top: 10px; padding: 5px; background: rgba(0,0,0,0.2); font-size: 12px; border-radius: 3px; min-height: 1em; }
            
            #trix-welcome-view h2 { margin-top: 0; color: #fff; }
            #trix-welcome-view p { color: #d1d5db; line-height: 1.6; }
            #trix-welcome-view code { background: var(--bg-dark); padding: 2px 5px; border-radius: 4px; font-family: Consolas, monospace; }

            #trix-ws-client-view { display: grid; grid-template-rows: auto auto 1fr auto; gap: 10px; height: 100%; }
            .trix-ws-client-grid { display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: start; }
            #trix-ws-url { width: 100%; }
            #trix-ws-connect-btn.connected { background-color: #dc3545; }
            #trix-ws-console { flex-grow: 1; background: #1e1e22; border: 1px solid var(--border-color); border-radius: 5px; padding: 10px; overflow-y: auto; font-family: monospace; font-size: 12px; white-space: pre-wrap; word-break: break-all; }
            .ws-console-send { color: #7ec699; } .ws-console-receive { color: #cc99cd; } .ws-console-system { color: #f08d49; }
            #trix-ws-message { width: 100%; min-height: 80px; resize: vertical; }
            .trix-ws-message-controls { display: flex; flex-direction: column; gap: 5px; }

            #trix-interceptor-view { flex-grow: 1; display: flex; flex-direction: column; }
            #trix-interceptor-controls { display: flex; gap: 10px; margin-bottom: 10px; }
            #trix-toggle-interception-btn.active { background-color: #dc3545; box-shadow: 0 0 8px #dc3545; }
            #trix-interceptor-queue { flex-grow: 1; background: #1e1e22; border: 1px solid var(--border-color); border-radius: 5px; padding: 10px; overflow-y: auto; font-family: monospace; font-size: 12px; }
            .interceptor-item { border: 1px solid var(--border-color); border-radius: 4px; padding: 8px; margin-bottom: 8px; background: var(--bg-dark); }
            .interceptor-item-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px; font-weight: bold; }
            .interceptor-item-header .send { color: #7ec699; } .interceptor-item-header .receive { color: #cc99cd; }
            .interceptor-item textarea { width: 100%; box-sizing: border-box; background: #2d2d2d; color: #ccc; border: 1px solid var(--border-color); min-height: 40px; font-family: inherit; }
            .interceptor-item-actions { display: flex; gap: 5px; margin-top: 5px; }
            .interceptor-btn { flex-grow: 1; padding: 4px; font-size: 11px; }
            .interceptor-forward-btn { background-color: #28a745; }
            .interceptor-block-btn { background-color: #dc3545; }

            #trix-packet-log-view, #trix-storage-view-container, #trix-injector-container { flex-grow: 1; display: flex; flex-direction: column; }
            #trix-packet-log { flex-grow: 1; background: #1e1e22; border: 1px solid var(--border-color); border-radius: 5px; padding: 10px; overflow-y: auto; font-family: monospace; font-size: 12px; }
            .trix-editor-area { position: relative; flex-grow: 1; background: #2d2d2d; border: 1px solid var(--border-color); border-radius: 5px; }
            .trix-editor-area textarea, .trix-editor-area pre { margin: 0; padding: 10px; font-family: 'Fira Code', 'Consolas', monospace; font-size: 14px; line-height: 1.5; width: 100%; height: 100%; box-sizing: border-box; position: absolute; top: 0; left: 0; overflow: auto; }
            .trix-editor-area textarea { z-index: 1; background: transparent; color: inherit; resize: none; border: none; outline: none; -webkit-text-fill-color: transparent; }
            .trix-editor-area pre { z-index: 0; pointer-events: none; }
            #trix-storage-view { flex-grow: 1; overflow-y: auto; font-family: monospace; font-size: 13px; }
            .storage-table { margin-top: 10px; } .storage-header { font-weight: bold; color: var(--accent-color); border-bottom: 1px solid var(--border-color); padding-bottom: 5px; margin-bottom: 5px; }
            .storage-row { display: flex; align-items: center; border-bottom: 1px solid #333; padding: 4px 2px; }
            .storage-key { color: #cc99cd; min-width: 100px; max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
            .storage-value { color: #7ec699; flex-grow: 1; cursor: pointer; word-break: break-all; }
            .storage-value-input { background: #3a3a42; color: #eee; border: 1px solid var(--accent-color); width: 100%; font-family: inherit; font-size: inherit; }
            .storage-delete { cursor: pointer; color: #ef4444; opacity: 0.5; margin-left: 10px; font-weight: bold; }
            .storage-delete:hover { opacity: 1; }
            
            #trix-music-toggle-btn { position: fixed; bottom: 20px; left: 20px; z-index: 99999; width: 50px; height: 50px; background-color: rgba(30, 30, 34, 0.8); color: #e5e7eb; border: 1px solid var(--border-color); border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(5px); transition: all 0.3s ease; }
            #trix-music-toggle-btn:hover { transform: scale(1.1); box-shadow: 0 0 10px var(--accent-color); color: var(--accent-color); }
            #trix-music-player { display: flex; flex-direction: column; position: fixed; bottom: 80px; left: 20px; width: 300px; background: rgba(30, 30, 34, 0.9); border: 1px solid var(--border-color); backdrop-filter: blur(10px); border-radius: 8px; padding: 15px; z-index: 99998; box-shadow: 0 5px 15px rgba(0,0,0,0.3); opacity: 0; transform: scale(0.95) translateY(10px); transition: opacity 0.3s ease, transform 0.3s ease; pointer-events: none; }
            #trix-music-player.visible { opacity: 1; transform: scale(1) translateY(0); pointer-events: auto; }
            .music-drag-handle { position: absolute; top: 0; left: 0; width: 100%; height: 20px; cursor: move; }
            .music-info { text-align: center; margin-bottom: 10px; }
            .music-title { font-size: 16px; font-weight: 600; display: block; }
            .music-artist { font-size: 12px; color: #9ca3af; }
            .music-progress-container { width: 100%; background: #4b5563; border-radius: 5px; cursor: pointer; height: 6px; margin-bottom: 5px; }
            .music-progress-bar { background: var(--accent-color); width: 0%; height: 100%; border-radius: 5px; }
            .music-time { display: flex; justify-content: space-between; font-size: 11px; color: #9ca3af; margin-bottom: 10px; }
            .music-controls { display: flex; justify-content: space-around; align-items: center; }
            .music-control-btn { background: none; border: none; color: #e5e7eb; cursor: pointer; padding: 5px; opacity: 0.8; transition: all 0.2s; }
            .music-control-btn:hover { opacity: 1; color: var(--accent-color); }
            .music-play-btn { font-size: 24px; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; }
            .music-volume-container { display: flex; align-items: center; position: relative; }
            .music-volume-slider { -webkit-appearance: none; appearance: none; width: 80px; height: 5px; background: #4b5563; border-radius: 5px; outline: none; transition: opacity 0.3s; }
            .music-volume-slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 12px; height: 12px; background: #e5e7eb; cursor: pointer; border-radius: 50%; }
            #trix-volume-indicator { position: absolute; top: -30px; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.7); color: white; padding: 4px 8px; border-radius: 4px; font-size: 12px; opacity: 0; transition: opacity 0.3s; pointer-events: none; }
            #trix-volume-indicator.visible { opacity: 1; }
            #trix-music-menu { 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; }
            #trix-music-menu.visible { opacity: 1; pointer-events: auto; }
            .music-menu-content { background: var(--bg-darker); border: 1px solid var(--border-color); width: 90%; max-width: 500px; max-height: 80%; border-radius: 8px; display: flex; flex-direction: column; }
            .music-menu-header { padding: 15px; border-bottom: 1px solid var(--border-color); }
            #music-search-input { width: 100%; box-sizing: border-box; }
            .music-song-list { overflow-y: auto; padding: 10px; }
            .music-song-item { padding: 10px 15px; cursor: pointer; border-radius: 4px; transition: background-color 0.2s; }
            .music-song-item:hover, .music-song-item.playing { background-color: var(--bg-dark); }
            .music-song-title { display: block; } .music-song-artist { font-size: 12px; color: #9ca3af; }
        `;
        shadowRoot.appendChild(styleElement);

        let state = {
            tabs: [], activeTabId: null,
            settings: { position: { top: '80px', right: '15px' }, size: { width: '500px', height: '450px' } },
            wsClient: { url: 'wss://echo.websocket.events', protocols: '', savedMessages: { 'Example JSON': '{"action":"ping","id":123}'} },
            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.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: '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', '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>
                <div id="trix-footer" style="padding:10px; text-align:center; background:rgba(0,0,0,0.2);"></div>`;
            
            const musicToggle = document.createElement('div');
            musicToggle.id = 'trix-music-toggle-btn';
            musicToggle.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle><circle cx="18" cy="16" r="3"></circle></svg>`;
            
            const musicPlayer = document.createElement('div');
            musicPlayer.id = 'trix-music-player';
            musicPlayer.innerHTML = `
                <div class="music-drag-handle"></div>
                <div class="music-info"><span class="music-title">Select a Song</span><span class="music-artist">...</span></div>
                <div class="music-progress-container"><div class="music-progress-bar"></div></div>
                <div class="music-time"><span id="music-current-time">0:00</span><span id="music-duration">0:00</span></div>
                <div class="music-controls">
                    <button class="music-control-btn" id="music-prev-btn" title="Previous"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M6 6h2v12H6zm3.5 6 8.5 6V6z"/></svg></button>
                    <button class="music-control-btn" id="music-rewind-btn" title="Back 10s"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M11.999 5v2.999l-4-3-4 3v10l4-3 4 3V19l-8-6 8-6z m8 0v2.999l-4-3-4 3v10l4-3 4 3V19l-8-6 8-6z"/></svg></button>
                    <button class="music-control-btn music-play-btn" id="music-play-pause-btn"><svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></button>
                    <button class="music-control-btn" id="music-forward-btn" title="Forward 10s"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="m3.999 19 8-6-8-6v12zm8 0 8-6-8-6v12z"/></svg></button>
                    <button class="music-control-btn" id="music-next-btn" title="Next"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg></button>
                </div>
                <div class="music-volume-container">
                    <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>
                    <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>`;

            shadowRoot.append(toggleBtn, container, musicToggle, musicPlayer, musicMenu);
            return { toggleBtn, container, musicPlayer };
        }
        
        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 incoming/outgoing game network traffic.</li>
                                <li><b>Interceptor:</b> Pause, edit, or block WebSocket messages in real-time.</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-grid">
                            <input type="text" id="trix-ws-url" class="trix-input" 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>
                        </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();
            } 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 === 'logger') {
                content.innerHTML = `
                    <div id="trix-packet-log-view" class="trix-view">
                        <div class="trix-tabs"></div>
                        <div id="trix-packet-log"></div>
                        <div class="trix-action-bar">
                             <button id="trix-clear-log-btn" class="trix-button">Clear Log</button>
                        </div>
                    </div>`;
                renderTabs();
            } 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-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 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 = () => { logToWsClientConsole('Connection closed.', 'system'); connectBtn.textContent = 'Connect'; connectBtn.classList.remove('connected'); connectBtn.disabled = false; customWs = null; };
            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);
        }

        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) { 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.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-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') {
                const log = $('#trix-packet-log'); if(log) log.innerHTML = '';
            }
            const wsSavedMessagesSelect = target.closest('#trix-ws-saved-messages');
            if(wsSavedMessagesSelect) {
                const name = wsSavedMessagesSelect.value;
                if (name) { $('#trix-ws-message').value = state.wsClient.savedMessages[name]; }
            }
        }
        
        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 executeScript() {
            const statusBar = $('.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}`;
            }
        }

        logPacketCallback = function(type, data) {
            const logContainer = $('#trix-packet-log');
            if (!logContainer) return;
            const item = document.createElement('div');
            item.className = `packet-item packet-${type}`;
            item.innerHTML = `<span class="packet-meta">[${type.toUpperCase()}]</span> ${data}`;
            logContainer.appendChild(item);
            logContainer.scrollTop = logContainer.scrollHeight;
        };

        loadState();
        ui = createUI();
        applySettings();
        renderActiveView();
        initEventListeners();
        
        const musicPlayer = initMusicPlayer();
        initDraggable(ui.musicPlayer, [$('.music-drag-handle', ui.musicPlayer)]);

        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);
    }

    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" }
                // Add more songs here in the same format
            ],
            isPlaying: false,
            currentSongIndex: 0,
            volume: 0.5
        };

        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 menuEl = $('#trix-music-menu');
        const songListEl = $('.music-song-list');
        let volumeTimeout;

        const loadSong = (index) => {
            player.currentSongIndex = index;
            const song = player.songList[index];
            player.audio.src = song.url;
            titleEl.textContent = song.title;
            artistEl.textContent = song.artist;
            renderSongList();
        };
        
        const playSong = () => {
            player.isPlaying = true;
            player.audio.play().catch(e => console.error("Audio play failed:", e));
            playPauseBtn.innerHTML = `<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>`;
        };
        const pauseSong = () => {
            player.isPlaying = false;
            player.audio.pause();
            playPauseBtn.innerHTML = `<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>`;
        };
        
        const nextSong = () => { let newIndex = player.currentSongIndex + 1; if (newIndex >= player.songList.length) newIndex = 0; loadSong(newIndex); playSong(); };
        const prevSong = () => { let newIndex = player.currentSongIndex - 1; if (newIndex < 0) newIndex = player.songList.length - 1; loadSong(newIndex); playSong(); };

        const setVolume = (value) => {
            player.volume = value;
            player.audio.volume = value;
            volumeSlider.value = value;
            volumeIndicator.textContent = `${Math.round(value * 100)}%`;
            volumeIndicator.classList.add('visible');
            clearTimeout(volumeTimeout);
            volumeTimeout = setTimeout(() => volumeIndicator.classList.remove('visible'), 1000);
        };
        
        const renderSongList = (filter = '') => {
            songListEl.innerHTML = '';
            const filteredList = player.songList.filter(song => song.title.toLowerCase().includes(filter) || song.artist.toLowerCase().includes(filter));
            filteredList.forEach((song) => {
                const originalIndex = player.songList.indexOf(song);
                const item = document.createElement('div');
                item.className = 'music-song-item';
                if (originalIndex === player.currentSongIndex) item.classList.add('playing');
                item.innerHTML = `<span class="music-song-title">${song.title}</span><span class="music-song-artist">${song.artist}</span>`;
                item.addEventListener('click', () => { loadSong(originalIndex); playSong(); menuEl.classList.remove('visible'); });
                songListEl.appendChild(item);
            });
        };

        $('#trix-music-toggle-btn').addEventListener('click', () => playerEl.classList.toggle('visible'));
        playPauseBtn.addEventListener('click', () => player.isPlaying ? pauseSong() : playSong());
        $('#music-next-btn').addEventListener('click', nextSong);
        $('#music-prev-btn').addEventListener('click', prevSong);
        $('#music-forward-btn').addEventListener('click', () => player.audio.currentTime += 10);
        $('#music-rewind-btn').addEventListener('click', () => player.audio.currentTime -= 10);
        volumeSlider.addEventListener('input', (e) => setVolume(e.target.value));
        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', nextSong);
        progressContainer.addEventListener('click', (e) => {
            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);
        loadSong(player.currentSongIndex);
        renderSongList();
        
        return player;
    }

    function waitForElement(selector, callback) {
        const maxRetries = 40; const retryInterval = 250; let retryCount = 0;
        const checkInterval = setInterval(() => {
            const element = document.querySelector(selector);
            if (element) {
                console.log(`[TriX] Target element '${selector}' found. Initializing script.`);
                clearInterval(checkInterval);
                callback();
            } else {
                retryCount++;
                if (retryCount >= maxRetries) {
                    clearInterval(checkInterval);
                    console.error(`[TriX] Failed to find target element '${selector}' after ${maxRetries} retries. Aborting initialization.`);
                }
            }
        }, retryInterval);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => waitForElement('#canvasA', initialize));
    } else {
        waitForElement('#canvasA', initialize);
    }

})();