// ==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);
}
})();