TriX Executor (REVAMP)

A modern, powerful developer toolkit and script executor for enhancing your web experience. Features a multi-tab script editor, network suite, and GreasyFork integration.

// ==UserScript==
// @name         TriX Executor (REVAMP)
// @namespace    https://greasyfork.org/en/users/COURTESYCOIL
// @version      5.1.5
// @description  A modern, powerful developer toolkit and script executor for enhancing your web experience. Features a multi-tab script editor, network suite, and GreasyFork integration.
// @author       Painsel
// @match        https://territorial.io/*
// @match        https://fxclient.github.io/FXclient/*
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_getClipboard
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @license      MIT
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzRmNDZlNSI+PHBhdGggZD0iTTE4LjM2IDIyLjA1bC00LjQyLTQuNDJDMTMuNDcgMTcuODcgMTMgMTguMTcgMTMgMTguNUMxMyAyMC45OSAxNS4wMSAyMyAxNy41IDIzaDMuNTRjLTIuNDUtMS40OC00LjQyLTMuNDUtNS4LTUuOTV6TTggMTNjMS42NiAwIDMuMTgtLjU5IDQuMzgtMS42MkwxMCAxMy41VjIybDIuNS0yLjVMMTggMTMuOTZjLjM1LS40OC42NS0xIC44Ny0xLjU1QzE4LjYxIDEzLjQxIDE4IDEyLjM0IDE4IDEyVjBoLTJ2MTJjMCAuMzQtLjAyLjY3LS4wNiAxLS4zMy4xOC0uNjguMy0xLjA0LjM3LTEuNzMgMC0zLjI3LS45My00LjE2LTIuMzZMNiAxMy42VjVINXY4eiIvPjwvc3ZnPg==
// ==/UserScript==

(function () {
  "use strict";

  const CURRENT_VERSION = GM_info.script.version;
  const UPDATE_URL = "https://update.greasyfork.org/scripts/549132/TriX%20Executor%20%28REVAMP%29.user.js";
  let isUpdateAvailable = false;

  // --- TriX Core: WebSocket Logging & Global State ---
  let isLoggerSuspended = false;
  let customWs = null;
  const monitoredConnections = new Map();
  let updateConnectionStatus = () => {};
  let updatePingDisplay = () => {};
  let logPacketCallback = () => {};
  let onConnectionStateChange = () => {};

  let settings = { theme: 'dark', antiScam: true, autoUpdate: true, blockPropaganda: true, socketToasts: true };
  const savedSettings = GM_getValue("settings", null);
  if (savedSettings) {
      settings = { ...settings, ...JSON.parse(savedSettings) };
  } else {
      GM_setValue("settings", JSON.stringify(settings));
  }

  const OriginalWebSocket = unsafeWindow.WebSocket;
  unsafeWindow.WebSocket = function(url, protocols) {
    let isGameSocket = false;
    try {
        const isTerritorial = window.location.hostname.includes('territorial.io') || document.querySelector('meta[content="FXclient"]');
        const urlObj = new URL(url, window.location.href);
        const urlString = urlObj.toString();
        const proxyParam = urlObj.searchParams.get('u');
        isGameSocket = isTerritorial && (urlString.includes('/s52/') || (proxyParam && atob(proxyParam).includes('/s52/')));
    } catch (e) { /* Invalid URL */ }

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

    console.log(`[TriX] Intercepting WebSocket: ${url}`);
    if (settings.socketToasts) {
        showNotification(`Socket Detected: ${url}`, "success");
    }
    const ws = new OriginalWebSocket(url, protocols);
    monitoredConnections.set(ws, { url, state: 'CONNECTING', log: [] });
    onConnectionStateChange();

    const originalSend = ws.send.bind(ws);
    ws.send = function(data) {
        if (settings.blockPropaganda && data && data.length === 2 && data[0] === 30 && data[1] === 40) {
            console.log('[TriX] Blocked propaganda packet.');
            return;
        }
        logPacketCallback(ws, 'send', data);
        return originalSend(data);
    };
    ws.addEventListener('message', event => { logPacketCallback(ws, 'receive', event.data); });
    ws.addEventListener('open', () => { const c = monitoredConnections.get(ws); if (c) c.state = 'OPEN'; onConnectionStateChange(); updateConnectionStatus('connected', 'Connection Established'); });
    ws.addEventListener('close', () => { monitoredConnections.delete(ws); onConnectionStateChange(); updateConnectionStatus('disconnected', 'Disconnected'); });
    ws.addEventListener('error', () => { monitoredConnections.delete(ws); onConnectionStateChange(); updateConnectionStatus('error', 'Connection Error'); });
    unsafeWindow.trixSocket = ws;
    return ws;
  };

  // --- UI State ---
  let isMinimized = false;
  let currentTab = "home";
  let activeNetworkTab = "logger";
  let scriptTabs = [{ id: "tab1", name: "Script 1", content: "// Welcome to the revamped TriX Executor!\nconsole.log('Phoenix Rising!');" }];
  let activeScriptTab = "tab1";

  if (!GM_getValue("scripts", null)) GM_setValue("scripts", JSON.stringify([]));

  // --- CSS Styles ---
  const styles = `
    .trix-executor { position: fixed; top: 50px; right: 50px; width: 650px; height: 500px; background: linear-gradient(135deg, #1e1e2e 0%, #2d2d44 100%); border-radius: 12px; box-shadow: 0 20px 40px rgba(0,0,0,0.3); z-index: 999999; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; color: #fff; overflow: hidden; transition: all 0.3s ease; display: none; flex-direction: column; }
    .trix-executor.ready { display: flex; }
    .trix-header { background: linear-gradient(90deg, #4f46e5 0%, #7c3aed 100%); height: 40px; display: flex; justify-content: space-between; align-items: center; padding: 0 10px 0 15px; border-radius: 12px 12px 0 0; flex-shrink: 0; cursor: move; user-select: none; }
    .trix-title-area { display: flex; align-items: center; gap: 15px; } #trix-user-profile { display: none; align-items: center; gap: 8px; } #trix-user-pfp { width: 24px; height: 24px; border-radius: 50%; border: 2px solid rgba(255,255,255,0.5); } #trix-user-name { font-size: 12px; font-weight: 600; }
    .trix-title { font-weight: 600; font-size: 14px; color: #fff; display: flex; align-items: center; }
    .revamp-badge { display: inline-block; margin-left: 8px; padding: 2px 6px; font-size: 9px; font-weight: 700; background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%); color: #422006; border-radius: 4px; vertical-align: middle; transform: translateY(-1px); }
    .trix-header-info { display: flex; align-items: center; gap: 15px; font-size: 12px; color: rgba(255,255,255,0.8); }
    #trix-conn-status { width: 10px; height: 10px; border-radius: 50%; background-color: #ef4444; transition: background-color 0.3s; } #trix-conn-status.connected { background-color: #10b981; } #trix-conn-status.error { background-color: #f59e0b; }
    .trix-controls { display: flex; align-items: center; gap: 8px; } .trix-btn { width: 20px; height: 20px; border-radius: 4px; border: none; cursor: pointer; font-size: 12px; font-weight: bold; display: flex; justify-content: center; align-items: center; padding: 0; transition: all 0.2s ease; }
    .minimize-btn { background: #fbbf24; color: #92400e; } .maximize-btn { background: #10b981; color: #065f46; } .close-btn { background: #ef4444; color: #991b1b; }
    .trix-btn:hover { transform: scale(1.1); box-shadow: 0 2px 8px rgba(0,0,0,0.3); }
    .trix-body { display: flex; height: 100%; overflow: hidden; }
    .trix-sidebar { width: 150px; background: rgba(0,0,0,0.2); padding: 15px 0; border-right: 1px solid rgba(255,255,255,0.1); flex-shrink: 0; }
    .sidebar-item { padding: 12px 20px; cursor: pointer; transition: all 0.2s ease; font-size: 13px; border-left: 3px solid transparent; }
    .sidebar-item:hover { background: rgba(255,255,255,0.1); border-left-color: #4f46e5; } .sidebar-item.active { background: rgba(79,70,229,0.3); border-left-color: #4f46e5; }
    .trix-content { flex: 1; padding: 20px; overflow-y: auto; }
    .content-section { display: none; height: 100%; box-sizing: border-box; } .content-section.active { display: block; }
    .script-input, .network-textarea { width: 100%; height: 200px; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.2); border-radius: 8px; padding: 15px; color: #fff; font-family: 'Courier New', monospace; font-size: 12px; resize: vertical; outline: none; box-sizing: border-box; }
    .script-tabs, .network-tabs { display: flex; gap: 5px; margin-bottom: 10px; flex-wrap: wrap; }
    .script-tab, .network-tab { background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.2); border-radius: 6px; padding: 6px 12px; font-size: 11px; cursor: pointer; transition: all 0.2s ease; position: relative; }
    .script-tab.active, .network-tab.active { background: rgba(79,70,229,0.5); border-color: #4f46e5; }
    .add-tab-btn { background: rgba(16,185,129,0.3); border: 1px solid #10b981; color: #10b981; border-radius: 6px; padding: 6px 12px; font-size: 11px; cursor: pointer; transition: all 0.2s ease; }
    .executor-buttons { display: flex; gap: 10px; margin-top: 15px; flex-wrap: wrap; }
    .exec-btn { background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); border: none; border-radius: 6px; padding: 10px 20px; color: #fff; font-size: 12px; font-weight: 600; cursor: pointer; transition: all 0.2s ease; }
    .exec-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(79,70,229,0.4); }
    .exec-btn:disabled { background: #4b5563; cursor: not-allowed; transform: none; box-shadow: none; }
    .exec-btn.secondary { background: linear-gradient(135deg, #6b7280 0%, #4b5563 100%); } .exec-btn.success { background: linear-gradient(135deg, #10b981 0%, #059669 100%); } .exec-btn.danger { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); }
    .search-input { width: 100%; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.2); border-radius: 8px; padding: 12px; color: #fff; font-size: 13px; margin-bottom: 15px; outline: none; box-sizing: border-box; }
    .script-cards { display: grid; gap: 15px; max-height: 300px; overflow-y: auto; }
    .script-card { background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.2); border-radius: 8px; padding: 15px; cursor: pointer; transition: all 0.2s ease; }
    .script-card:hover { background: rgba(255,255,255,0.1); transform: translateY(-2px); }
    .card-title { font-weight: 600; margin-bottom: 5px; color: #818cf8; } .card-description { font-size: 12px; color: rgba(255,255,255,0.7); }
    .settings-group { margin-bottom: 20px; } .settings-label { display: flex; align-items: center; margin-bottom: 8px; font-weight: 600; font-size: 13px; } .settings-checkbox { margin-right: 10px; width: 16px; height: 16px; accent-color: #4f46e5; }
    .settings-input, .settings-select { width: 100%; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.2); border-radius: 6px; padding: 10px; color: #fff; font-size: 12px; outline: none; box-sizing: border-box; }
    .notification-container { position: fixed; bottom: 20px; right: 20px; z-index: 2147483647; display: flex; flex-direction: column; gap: 10px; align-items: flex-end; }
    .notification { background: linear-gradient(135deg, #2d2d44 0%, #1e1e2e 100%); border-left: 4px solid #10b981; color: #fff; padding: 12px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); font-size: 13px; max-width: 350px; word-break: break-all; opacity: 0; transform: translateX(100%); transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
    .notification.show { opacity: 1; transform: translateX(0); }
    .minimized-icon { position: fixed; width: 50px; height: 50px; background-color: #1e1e2e; border: 3px solid #4f46e5; border-radius: 12px; cursor: pointer; z-index: 999999; transition: all 0.2s ease; box-shadow: 0 8px 25px rgba(0,0,0,0.3); display: flex; justify-content: center; align-items: center; font-size: 24px; font-weight: bold; color: #818cf8; user-select: none; }
    .minimized-icon:hover { transform: scale(1.05); box-shadow: 0 12px 35px rgba(79,70,229,0.4); }
    .network-view-content { margin-top: 15px; height: calc(100% - 50px); display: flex; flex-direction: column; }
    .packet-log { height: 100%; overflow-y: auto; background: rgba(0,0,0,0.2); padding: 10px; border-radius: 8px; font-family: 'Courier New', monospace; font-size: 12px; }
    .log-item { padding: 4px; border-bottom: 1px solid rgba(255,255,255,0.1); word-break: break-all; } .log-item.send { color: #6ee7b7; } .log-item.receive { color: #f0abfc; }
    .storage-table { display: grid; grid-template-columns: 150px 1fr auto; gap: 10px; align-items: center; font-size: 12px; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.1); }
    .storage-key { color: #f0abfc; font-weight: bold; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .storage-value { color: #6ee7b7; word-break: break-all; }
    .trix-content::-webkit-scrollbar, .script-cards::-webkit-scrollbar, .packet-log::-webkit-scrollbar { width: 8px; }
    .trix-content::-webkit-scrollbar-track, .script-cards::-webkit-scrollbar-track, .packet-log::-webkit-scrollbar-track { background: rgba(0,0,0,0.2); border-radius: 4px; }
    .trix-content::-webkit-scrollbar-thumb, .script-cards::-webkit-scrollbar-thumb, .packet-log::-webkit-scrollbar-thumb { background: rgba(79,70,229,0.6); border-radius: 4px; }
    #suggestion-box { margin-top: 40px; padding-top: 20px; border-top: 1px solid rgba(255,255,255,0.1); text-align: center; }
    .advert-toast { display: flex; gap: 15px; align-items: center; background: linear-gradient(135deg, #1e1e2e 0%, #2d2d44 100%); border-left-color: #7c3aed; }
    .advert-img { width: 80px; height: 80px; border-radius: 6px; object-fit: cover; }
    .advert-content { flex-grow: 1; } .advert-content p { font-size: 13px; margin: 0 0 10px 0; }
    .trix-modal-backdrop { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 1000000; display: none; align-items: center; justify-content: center; backdrop-filter: blur(5px); }
    .trix-modal-backdrop.visible { display: flex; }
    .trix-modal-content { background: #1e1e2e; border: 1px solid #4f46e5; width: 90%; max-width: 450px; border-radius: 12px; display: flex; flex-direction: column; box-shadow: 0 20px 40px rgba(0,0,0,0.3); }
    .trix-modal-header { padding: 15px; border-bottom: 1px solid #4f46e5; font-size: 16px; font-weight: 600; } .trix-modal-body { padding: 20px; }
    .trix-modal-footer { padding: 15px; border-top: 1px solid #4f46e5; display: flex; justify-content: flex-end; gap: 10px; }
    #update-reminder { background: rgba(251, 191, 36, 0.1); border: 1px solid #fbbf24; padding: 15px; border-radius: 8px; margin-bottom: 20px; }
  `;
  const styleSheet = document.createElement("style");
  styleSheet.textContent = styles;
  document.head.appendChild(styleSheet);
  
  // --- UI Creation ---
  function createExecutorPanel() {
    const panel = document.createElement("div"); panel.className = "trix-executor"; panel.innerHTML = ` <div class="trix-header"> <div class="trix-title-area"> <div id="trix-user-profile"> <img id="trix-user-pfp" src=""> <span id="trix-user-name"></span> </div> <div class="g-signin2" data-onsuccess="onSignIn" data-theme="dark"></div> <div class="trix-title">TriX Executor v5.1.2 <span class="revamp-badge">REVAMP</span></div> </div> <div class="trix-header-info"> <div id="trix-conn-status" title="Disconnected"></div> <span id="trix-ping-display">Ping: ---</span> <span id="trix-fps-display">FPS: --</span> </div> <div class="trix-controls"> <button class="trix-btn minimize-btn" title="Minimize">−</button> <button class="trix-btn maximize-btn" title="Maximize">□</button> <button class="trix-btn close-btn" title="Close">×</button> </div> </div> <div class="trix-body"> <div class="trix-sidebar"> <div class="sidebar-item active" data-tab="home">🏠 Home</div> <div class="sidebar-item" data-tab="main">⚡ Main</div> <div class="sidebar-item" data-tab="network">📡 Network</div> <div class="sidebar-item" data-tab="cloud">☁ Cloud</div> <div class="sidebar-item" data-tab="files">📁 Files</div> <div class="sidebar-item" data-tab="settings">⚙ Settings</div> </div> <div class="trix-content"> ${createHomeContent()} ${createMainContent()} ${createNetworkContent()} ${createCloudContent()} ${createFilesContent()} ${createSettingsContent()} </div> </div> `;
    document.body.appendChild(panel);
    return panel;
  }
  
  // --- MINIFIED HELPER FUNCTIONS ---
  function showNotification(m, t="success", d=5000) { const c = document.querySelector('.notification-container') || document.createElement('div'); if (!c.className) { c.className = 'notification-container'; document.body.appendChild(c); } const n = document.createElement("div"); n.className="notification"; n.innerHTML=m; if(t==="error") n.style.borderLeftColor = "#ef4444"; c.appendChild(n); setTimeout(()=>n.classList.add("show"),10); setTimeout(()=>{n.classList.remove("show"); setTimeout(()=>n.remove(),400)},d); }
  function createHomeContent(){return`<div class="content-section active" id="home-content"><h2>Script Store</h2><input type="text" class="search-input" placeholder="Search saved scripts..." id="script-search"><div class="script-cards" id="saved-scripts"></div></div>`}
  function createMainContent(){return`<div class="content-section" id="main-content"><h2>Live JS Executor</h2><div class="script-tabs" id="script-tabs"></div><textarea class="script-input" placeholder="Enter Script Here..." id="script-editor"></textarea><div class="executor-buttons"><button class="exec-btn" id="execute-btn">Execute</button><button class="exec-btn secondary" id="execute-clipboard-btn">Execute Clipboard</button><button class="exec-btn success" id="save-script-btn">Save Script</button></div></div>`}
  function createCloudContent(){return`<div class="content-section" id="cloud-content"><h2>GreasyFork Scripts</h2><input type="text" class="search-input" placeholder="Search GreasyFork..." id="greasyfork-search"><div class="script-cards" id="cloud-results"></div></div>`}
  function createFilesContent(){return`<div class="content-section" id="files-content"><h2>File Explorer</h2><p style="font-size:12px; color:#ccc; margin-bottom:10px">A read-only view of data this script has stored.</p><div class="script-cards" id="file-explorer-view"></div></div>`}
  function createNetworkContent(){return`<div class="content-section" id="network-content"><div class="network-tabs"><div class="network-tab active" data-net-tab="logger">Logger</div><div class="network-tab" data-net-tab="injector">Injector</div><div class="network-tab" data-net-tab="ws_client">WS Client</div><div class="network-tab" data-net-tab="storage">Storage</div></div><div class="network-view-content"></div></div>`}
  function createSettingsContent(){const s=settings;return`<div class="content-section" id="settings-content"><h2>Settings</h2><div id="update-reminder-container"></div><div class="settings-group"><label class="settings-label"><input type="checkbox" class="settings-checkbox" id="socket-toasts" ${s.socketToasts?"checked":""}>Socket Connection Toasts</label></div><div class="settings-group"><label class="settings-label"><input type="checkbox" class="settings-checkbox" id="block-propaganda" ${s.blockPropaganda?"checked":""}>Block Propaganda Popups (for territorial.io)</label></div><div class="settings-group"><label class="settings-label"><input type="checkbox" class="settings-checkbox" id="anti-scam" ${s.antiScam?"checked":""}>Anti-Scam Protection</label></div><div id="suggestion-box"><p>Have any suggestions? Send a message to Painsel!</p><button id="submit-suggestion-btn" class="exec-btn">Submit a Suggestion</button></div></div>`}
  function renderActiveNetworkView(){const c=document.querySelector("#network-content .network-view-content");if(!c)return;c.innerHTML="";switch(activeNetworkTab){case "logger":c.innerHTML=`<div class="executor-buttons" style="margin-top:0;margin-bottom:10px"><button class="exec-btn" id="suspend-log-btn">Suspend Log</button><button class="exec-btn secondary" id="clear-log-btn">Clear Log</button></div><div class="packet-log" id="packet-log-output">Waiting for connection...</div>`;renderPacketLog();break;case "injector":c.innerHTML=`<h3>Packet Injector</h3><p style="font-size:12px;color:#ccc;margin-bottom:10px">Send a custom packet to the current game connection.</p><textarea class="network-textarea" id="injector-input" placeholder='e.g., 42["chat","Hello from TriX!"]' style="height:150px"></textarea><div class="executor-buttons"><button class="exec-btn" id="inject-packet-btn">Inject Packet</button></div>`;break;case "ws_client":c.innerHTML=`<h3>WebSocket Client</h3><input type="text" id="ws-client-url" class="search-input" placeholder="wss://your-socket-url.com"><div class="executor-buttons" style="margin-top:0"><button class="exec-btn" id="ws-client-connect-btn">Connect</button></div><div class="packet-log" id="ws-client-log" style="height:150px;margin-top:10px"></div><textarea class="network-textarea" id="ws-client-input" placeholder="Message to send..." style="height:80px;margin-top:10px"></textarea><div class="executor-buttons"><button class="exec-btn" id="ws-client-send-btn">Send</button></div>`;break;case "storage":c.innerHTML=`<h3>Storage Explorer</h3><h4>Local Storage</h4><div id="local-storage-view"></div><h4 style="margin-top:20px">Session Storage</h4><div id="session-storage-view"></div>`;renderStorageView();break;}}
  function renderPacketLog(){const l=document.getElementById("packet-log-output");if(!l)return;const a=Array.from(monitoredConnections.values()).find(c=>c.state==='OPEN'||c.log.length>0);if(!a){l.textContent='Waiting for active connection...';return}if(isLoggerSuspended)return;l.innerHTML='';a.log.forEach(p=>{const i=document.createElement('div');i.className=`log-item ${p.type}`;i.textContent=`[${p.type.toUpperCase()}] ${p.data}`;l.appendChild(i)});l.scrollTop=l.scrollHeight}
  function renderStorageView(){const l=document.getElementById('local-storage-view'),s=document.getElementById('session-storage-view');if(!l||!s)return;const c=(st,t)=>{let h='';for(let i=0;i<st.length;i++){const k=st.key(i),v=st.getItem(k);h+=`<div class="storage-table"><div class="storage-key" title="${k}">${k}</div><div class="storage-value" title="${v}">${v}</div><button class="exec-btn danger storage-delete-btn" data-type="${t}" data-key="${k}" style="padding:5px 8px;font-size:10px">X</button></div>`}return h||'<div style="font-size:12px;color:#888;padding:10px 0">Empty</div>'};l.innerHTML=c(localStorage,'local');s.innerHTML=c(sessionStorage,'session')}
  function loadFileExplorer(){const c=document.getElementById("file-explorer-view");if(!c)return;c.innerHTML="";const s=JSON.parse(GM_getValue("scripts","[]")),t=JSON.parse(GM_getValue("settings","{}"));const f=[{name:"scripts.json",type:"GM Storage",size:`${JSON.stringify(s).length} bytes`},{name:"settings.json",type:"GM Storage",size:`${JSON.stringify(t).length} bytes`},...s.map(s=>({name:s.name,type:'Saved Script',size:`${s.code.length} chars`}))];f.forEach(file=>{const a=document.createElement("div");a.className="script-card";a.style.cursor="default";a.innerHTML=`<div class="card-title">📄 ${file.name}</div><div class="card-description">${file.type} • ${file.size}</div>`;c.appendChild(a)})}
  function createMinimizedIcon(){const i=document.createElement("div");i.className="minimized-icon";i.title="TriX Executor";i.style.top="50px";i.style.right="50px";i.textContent="T";document.body.appendChild(i);let d=!1,o={x:0,y:0};i.addEventListener("mousedown",e=>{d=!0;const r=i.getBoundingClientRect();o={x:e.clientX-r.left,y:e.clientY-r.top};e.preventDefault()});document.addEventListener("mousemove",e=>{if(d){const x=e.clientX-o.x,y=e.clientY-o.y;i.style.left=Math.max(0,Math.min(x,window.innerWidth-i.offsetWidth))+"px";i.style.top=Math.max(0,Math.min(y,window.innerHeight-i.offsetHeight))+"px";i.style.right="auto"}});document.addEventListener("mouseup",e=>{if(d){d=!1;if(e.target===i)restorePanel()}});return i}
  function minimizePanel(){const p=document.querySelector(".trix-executor");if(p){p.style.display="none";isMinimized=!0;createMinimizedIcon()}}
  function restorePanel(){const p=document.querySelector(".trix-executor"),i=document.querySelector(".minimized-icon");if(p)p.style.display="flex";if(i)i.remove();isMinimized=!1}
  function closePanel(){const p=document.querySelector(".trix-executor");if(p)p.remove()}
  function switchTab(tabName){document.querySelectorAll(".sidebar-item").forEach(i=>i.classList.remove("active"));document.querySelector(`[data-tab="${tabName}"]`).classList.add("active");document.querySelectorAll(".content-section").forEach(s=>s.classList.remove("active"));document.getElementById(`${tabName}-content`).classList.add("active");currentTab=tabName;if(tabName==="home")loadSavedScripts();else if(tabName==="main")updateScriptTabs();else if(tabName==="network")renderActiveNetworkView();else if(tabName==="files")loadFileExplorer();}
  function updateScriptTabs(){const t=document.getElementById("script-tabs");t.innerHTML="";scriptTabs.forEach(tab=>{const e=document.createElement("div");e.className=`script-tab ${tab.id===activeScriptTab?"active":""}`;e.textContent=tab.name;e.onclick=()=>switchScriptTab(tab.id);e.ondblclick=()=>renameScriptTab(tab.id);t.appendChild(e)});const a=document.createElement("div");a.className="add-tab-btn";a.textContent="+";a.onclick=addScriptTab;t.appendChild(a);const c=scriptTabs.find(tab=>tab.id===activeScriptTab);if(c)document.getElementById("script-editor").value=c.content}
  function switchScriptTab(tabId){const c=scriptTabs.find(tab=>tab.id===activeScriptTab);if(c)c.content=document.getElementById("script-editor").value;activeScriptTab=tabId;updateScriptTabs()}
  function addScriptTab(){const n="tab"+Date.now(),t={id:n,name:`Script ${scriptTabs.length+1}`,content:""};scriptTabs.push(t);activeScriptTab=n;updateScriptTabs();showNotification("New tab created")}
  function renameScriptTab(tabId){const t=scriptTabs.find(t=>t.id===tabId);if(t){const n=prompt("Enter new tab name:",t.name);if(n&&n.trim()){t.name=n.trim();updateScriptTabs();showNotification("Tab renamed")}}}
  function executeScript(code){try{if(settings.antiScam&&[/document\.cookie/i,/localStorage\./i,/sessionStorage\./i,/\.send\(/i,/fetch\(/i,/XMLHttpRequest/i].some(p=>p.test(code))&&!confirm("This script contains potentially suspicious code that could access your data. Are you sure you want to execute it?")){showNotification("Execution cancelled by user.","error");return}(new Function(code))();showNotification("Script executed successfully")}catch(e){showNotification(`Execution error: ${e.message}`,"error");console.error("[TriX] Script execution error:",e)}}
  function saveScript(){const c=document.getElementById("script-editor").value;if(!c.trim()){showNotification("No script to save","error");return}const n=prompt("Enter script name:");if(!n||!n.trim())return;const s=JSON.parse(GM_getValue("scripts")),a={id:Date.now().toString(),name:n.trim(),code:c,created:new Date().toISOString()};s.push(a);GM_setValue("scripts",JSON.stringify(s));showNotification("Script saved successfully");if(currentTab==="home")loadSavedScripts()}
  function loadSavedScripts(){const s=JSON.parse(GM_getValue("scripts")),c=document.getElementById("saved-scripts");if(!c)return;c.innerHTML="";s.forEach(script=>{const a=document.createElement("div");a.className="script-card";a.innerHTML=`<div class="card-title">${script.name}</div><div class="card-description">Created: ${new Date(script.created).toLocaleDateString()}</div><button class="exec-btn danger delete-btn" style="margin-top:10px;padding:5px 10px;font-size:11px">Delete</button>`;a.addEventListener("click",e=>{if(e.target.classList.contains("delete-btn"))return;switchTab("main");const n={id:`loaded-${script.id}`,name:script.name,content:script.code},t=scriptTabs.find(t=>t.id===n.id);if(!t)scriptTabs.push(n);activeScriptTab=n.id;updateScriptTabs();showNotification(`Script "${script.name}" loaded`)});a.querySelector(".delete-btn").addEventListener("click",e=>{e.stopPropagation();if(confirm(`Are you sure you want to delete "${script.name}"?`)){const t=s.filter(s=>s.id!==script.id);GM_setValue("scripts",JSON.stringify(t));showNotification("Script deleted");loadSavedScripts()}});c.appendChild(a)});if(s.length===0)c.innerHTML='<div style="text-align:center;color:rgba(255,255,255,0.5);padding:40px">No saved scripts</div>'}
  async function searchGreasyFork(q){if(!q.trim())return;const r=document.getElementById("cloud-results");r.innerHTML='<div style="text-align:center;padding:20px">Searching...</div>';try{const e=await fetch(`https://greasyfork.org/en/scripts.json?q=${encodeURIComponent(q)}`),s=await e.json();r.innerHTML="";if(!s.length){r.innerHTML='<div style="text-align:center;padding:20px;color:rgba(255,255,255,0.5)">No scripts found</div>';return}s.slice(0,50).forEach(c=>{const d=document.createElement("div");d.className="script-card";d.innerHTML=`<div class="card-title">${c.name}</div><div class="card-description">${c.description||'No description.'}</div><div style="font-size:11px;color:rgba(255,255,255,0.6);margin-top:8px">By ${c.users[0].name} • ${c.total_installs} installs</div><button class="exec-btn success" data-url="${c.code_url}" data-name="${c.name}" style="margin-top:10px;padding:5px 10px;font-size:11px">Load Script</button>`;d.querySelector("button").onclick=async e=>{showNotification("Loading script...");try{const t=await fetch(e.target.dataset.url),a=await t.text();switchTab("main");addScriptTab();const f=scriptTabs.find(t=>t.id===activeScriptTab);f.name=e.target.dataset.name;f.content=a;updateScriptTabs();showNotification("Script loaded into new tab!")}catch(err){showNotification("Failed to load script code.","error")}};r.appendChild(d)})}catch(e){r.innerHTML='<div style="text-align:center;padding:20px;color:red">Failed to fetch scripts</div>'}}
  unsafeWindow.onSignIn = function(g){const p=g.getBasicProfile(),u=p.getName(),i=p.getImageUrl(),d=document.querySelector('.trix-executor #trix-user-profile'),b=document.querySelector('.trix-executor .g-signin2'),t=document.querySelector('.trix-executor .trix-title');if(d&&b){document.getElementById('trix-user-pfp').src=i;document.getElementById('trix-user-name').textContent=u;d.style.display='flex';b.style.display='none';if(t)t.style.display='none'}}
  function showLoadingScreen(c){const l=document.createElement('div');l.id='trix-loading-screen';const m=["Report all harmful scripts to Painsel! Find the \"Submit a Suggestion\" button in Settings!","Block propaganda with TriX Executor! You can toggle this feature in Settings!","Definitely not a cheating tool..."];l.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-message">${m[0]}</div></div>`;const d=document.createElement('div');d.id='trix-loading-settings-modal';d.style.visibility='hidden';d.innerHTML=`<div class="loading-content"><div class="loading-header">Settings</div><div class="settings-group" style="text-align:left;max-width:300px;margin:2rem auto"><label class="settings-label">Theme</label><select class="settings-select" id="trix-theme-select-loading"><option value="dark">Dark (Default)</option></select></div><button id="trix-resume-loading" class="exec-btn" style="margin-top:2rem">Resume</button></div>`;const s=document.createElement('style');s.textContent=`#trix-loading-screen,#trix-loading-settings-modal{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#11111b;z-index:2147483647;display:flex;flex-direction:column;align-items:center;justify-content:center;color:#fff;font-family:'Segoe UI',sans-serif;opacity:0;transition:opacity .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:700;color:#4f46e5;text-shadow:2px 2px 0 #4338ca,4px 4px 0 #312e81,6px 6px 10px rgba(0,0,0,.5);margin-bottom:2rem}.loading-bar-container{width:50%;max-width:600px;height:20px;background-color:#2d2d44;border-radius:10px;border:1px solid #4f46e5;overflow:hidden;margin-bottom:1rem}.loading-bar-progress{width:0%;height:100%;background:linear-gradient(90deg,#7c3aed,#4f46e5);border-radius:10px;transition:width .1s linear}.loading-message{font-style:italic;color:#9ca3af;min-height:1.2em;margin-top:1rem}`;document.head.appendChild(s);document.body.append(l,d);setTimeout(()=>l.classList.add("visible"),10);const p=l.querySelector('.loading-bar-progress'),g=l.querySelector('.loading-message');let r=0,idx=0;const totalDuration=12000;let timeSinceLastMsg=0;const i=setInterval(()=>{r+=100/(totalDuration/100);timeSinceLastMsg+=100;const currentMsgDuration=m[idx].length*60+1000;if(timeSinceLastMsg>currentMsgDuration&&idx<m.length-1){idx++;g.textContent=m[idx];timeSinceLastMsg=0}p.style.width=`${Math.min(r,100)}%`;if(r>=100){clearInterval(i);window.removeEventListener('keydown',f);l.querySelector('.loading-content').classList.add('fade-out');setTimeout(()=>{l.classList.remove('visible');setTimeout(()=>{l.remove();d.remove();s.remove();c()},500)},500)}},100);const f=e=>{if(e.key==='F12'){e.preventDefault();clearInterval(i);d.style.visibility='visible';d.classList.add('visible')}};window.addEventListener('keydown',f);d.querySelector('#trix-resume-loading').onclick=()=>{d.classList.remove('visible');showLoadingScreen(c)};setTimeout(()=>{const ad=`<div class="advert-content"><p>Install TrixMusic and play your favorite songs while playing! Submit a suggestion to Painsel and he will add all the songs you request!</p><button class="exec-btn success" onclick="window.open('https://update.greasyfork.org/scripts/555311/TrixMusic.user.js','_blank')">Install now!</button></div><img class="advert-img" src="https://evzxirgylircpnblikrw.supabase.co/storage/v1/object/public/OMEHGS/Gemini_Generated_Image_szz3cuszz3cuszz3.png">`;showNotification(ad,"advert",10000);const notif=document.querySelector('.notification-container .notification:last-child');if(notif)notif.classList.add('advert-toast')},2000)}
  function showUpdateModal(){const m=document.createElement('div');m.className='trix-modal-backdrop visible';m.innerHTML=`<div class="trix-modal-content"><div class="trix-modal-header" style="color:#fbbf24;">WARNING: Outdated Version!</div><div class="trix-modal-body"><p>You are using an older version of TriX Executor! Please update to the latest version for the best experience.</p></div><div class="trix-modal-footer"><button id="modal-cancel-update" class="exec-btn secondary">Cancel</button><button id="modal-confirm-update" class="exec-btn success">Update now!</button></div></div>`;document.body.appendChild(m);m.addEventListener('click',e=>{if(e.target.id==='modal-confirm-update')window.open(UPDATE_URL,'_blank');if(e.target.id==='modal-cancel-update'||e.target===m){isUpdateAvailable=!0;switchTab('settings');m.remove()}})}
  function attachEventListeners(){const p=document.querySelector('.trix-executor');if(!p)return;p.addEventListener('click',async e=>{if(e.target.matches('.minimize-btn'))minimizePanel();if(e.target.matches('.maximize-btn')){if(p.style.width==="100vw")Object.assign(p.style,{width:"650px",height:"500px",top:"50px",left:"auto",right:"50px"});else Object.assign(p.style,{width:"100vw",height:"100vh",top:"0",left:"0",right:"auto"})}if(e.target.matches('.close-btn'))closePanel();if(e.target.matches('#execute-btn'))executeScript(document.getElementById("script-editor").value);if(e.target.matches('#execute-clipboard-btn')){try{const t=await GM_getClipboard();if(t&&t.trim())executeScript(t);else showNotification("Clipboard is empty","error")}catch(err){showNotification("Failed to read clipboard","error")}}if(e.target.matches('#save-script-btn'))saveScript();if(e.target.matches('#suspend-log-btn')){isLoggerSuspended=!isLoggerSuspended;e.target.textContent=isLoggerSuspended?"Resume Log":"Suspend Log";e.target.classList.toggle('secondary',isLoggerSuspended)}if(e.target.matches('#clear-log-btn')){const a=Array.from(monitoredConnections.values()).find(c=>c.state==='OPEN');if(a)a.log=[];renderPacketLog()}if(e.target.matches('#inject-packet-btn')){if(unsafeWindow.trixSocket&&unsafeWindow.trixSocket.readyState===1){unsafeWindow.trixSocket.send(document.getElementById("injector-input").value);showNotification("Packet injected")}else showNotification("Not connected to game server.","error")}if(e.target.matches('.storage-delete-btn')){const{type,key}=e.target.dataset,s=type==='local'?localStorage:sessionStorage;if(confirm(`Delete "${key}" from ${type} storage?`)){s.removeItem(key);renderStorageView()}}if(e.target.matches('.sidebar-item'))switchTab(e.target.dataset.tab);if(e.target.matches('.network-tab')){p.querySelectorAll('.network-tab').forEach(t=>t.classList.remove('active'));e.target.classList.add('active');activeNetworkTab=e.target.dataset.netTab;renderActiveNetworkView()}if(e.target.matches('#submit-suggestion-btn')||e.target.matches('#update-now-reminder')){window.open(e.target.matches('#submit-suggestion-btn')?'https://form.jotform.com/253124626389563':UPDATE_URL,'_blank');}});p.addEventListener('input',e=>{if(e.target.matches('#script-editor')){const t=scriptTabs.find(tab=>tab.id===activeScriptTab);if(t)t.content=e.target.value}if(e.target.matches('#script-search')){const q=e.target.value.toLowerCase();p.querySelectorAll("#saved-scripts .script-card").forEach(c=>{const t=c.querySelector(".card-title").textContent.toLowerCase();c.style.display=t.includes(q)?"block":"none"})}});p.addEventListener('change',e=>{if(e.target.matches('#settings-content input')){settings.antiScam=document.getElementById("anti-scam").checked;settings.blockPropaganda=document.getElementById("block-propaganda").checked;settings.socketToasts=document.getElementById("socket-toasts").checked;GM_setValue("settings",JSON.stringify(settings));showNotification("Settings saved")}});p.addEventListener('keypress',e=>{if(e.target.matches('#greasyfork-search')&&e.key==='Enter'){searchGreasyFork(e.target.value)}});let isDragging=!1,dragOffset={x:0,y:0};p.querySelector(".trix-header").onmousedown=e=>{if(e.target.closest(".trix-btn, .g-signin2"))return;isDragging=!0;const r=p.getBoundingClientRect();dragOffset={x:e.clientX-r.left,y:e.clientY-r.top};e.preventDefault()};document.addEventListener('mousemove',e=>{if(isDragging&&!isMinimized){const x=e.clientX-dragOffset.x,y=e.clientY-dragOffset.y;p.style.left=Math.max(0,Math.min(x,window.innerWidth-p.offsetWidth))+"px";p.style.top=Math.max(0,Math.min(y,window.innerHeight-p.offsetHeight))+"px";p.style.right="auto"}});document.addEventListener('mouseup',()=>{isDragging=!1})}

  function init() {
    createExecutorPanel();
    const googleApiScript=document.createElement('script');googleApiScript.src="https://apis.google.com/js/platform.js";googleApiScript.async=!0;googleApiScript.defer=!0;document.head.appendChild(googleApiScript);
    attachEventListeners();
    document.onkeydown=e=>{if(e.ctrlKey&&e.shiftKey&&e.key.toUpperCase()==="E"){e.preventDefault();const p=document.querySelector('.trix-executor');if(!p){showLoadingScreen(init)}else if(isMinimized)restorePanel();else minimizePanel()}};
    logPacketCallback=(ws,type,data)=>{const c=monitoredConnections.get(ws);if(c){c.log.push({type,data});if(c.log.length>200)c.log.shift();if(currentTab==='network'&&activeNetworkTab==='logger'&&!isLoggerSuspended)renderPacketLog()}};
    onConnectionStateChange=()=>{if(currentTab==='network'&&activeNetworkTab==='logger')renderPacketLog()};
    updateConnectionStatus=(status,title)=>{const e=document.getElementById('trix-conn-status');if(e){e.className=status;e.title=title}};
    updatePingDisplay=async()=>{const e=document.getElementById('trix-ping-display');if(!e)return;const s=performance.now();try{await fetch(`${window.location.protocol}//${window.location.host}/favicon.ico?_=${Date.now()}`,{method:'HEAD',cache:'no-store'});e.textContent=`Ping: ${Math.round(performance.now()-s)}`}catch{e.textContent='Ping: ---'}};
    setInterval(updatePingDisplay,5000);
    let lastFrameTime=performance.now(),frameCount=0;
    function updateFPS(now){const fpsDisplay=document.getElementById('trix-fps-display');frameCount++;if(now>=lastFrameTime+1000){if(fpsDisplay)fpsDisplay.textContent=`FPS: ${frameCount}`;lastFrameTime=now;frameCount=0}requestAnimationFrame(updateFPS)}
    requestAnimationFrame(updateFPS);
    GM_xmlhttpRequest({method:'GET',url:'https://greasyfork.org/en/scripts/549132.json',onload:res=>{const latest=JSON.parse(res.responseText).version;if(CURRENT_VERSION!==latest)showUpdateModal()}});
    loadSavedScripts();
    updateScriptTabs();
    document.querySelector('.trix-executor').classList.add('ready');
    showNotification("TriX Executor loaded! Press Ctrl+Shift+E to toggle.");
  }
  
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", () => showLoadingScreen(init));
  } else {
    showLoadingScreen(init);
  }
})();