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.2.7
// @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
// @icon https://evzxirgylircpnblikrw.supabase.co/storage/v1/object/public/OMEHGS/wmremove-transformed%20(1).png
// ==/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 shouldShowUpdateReminder = GM_getValue("showUpdateReminder", 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, blockPropaganda: true, socketToasts: true, showAd: true, showOnAllSites: 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: 8px; }
.trix-icon { width: 24px; height: 24px; }
.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; display: flex; flex-direction: column; }
.sidebar-nav { flex-grow: 1; }
.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; }
.sidebar-footer { padding: 10px; border-top: 1px solid rgba(255,255,255,0.1); }
#trix-user-profile { display: flex; align-items: center; gap: 8px; padding: 5px; }
#trix-user-pfp { width: 32px; height: 32px; border-radius: 50%; border: 2px solid rgba(255,255,255,0.5); }
#trix-user-name { font-size: 12px; font-weight: 600; color: #c7d2fe; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g-signin2 { margin-top: 10px; transform: scale(0.8); transform-origin: center; }
.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: 20px; 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-top: 20px; text-align: center; }
#cloud-notice { background: rgba(79, 70, 229, 0.1); border: 1px solid #4f46e5; padding: 15px; border-radius: 8px; margin-bottom: 20px; font-size: 13px; text-align: center; }
`;
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"> <img src="${GM_info.script.icon}" class="trix-icon"> <div class="trix-title">TriX Executor v5.1.6 <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-nav"> <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="sidebar-footer"> <div id="trix-user-profile"> <img id="trix-user-pfp" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNhNWY0ZmMiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMjAgMjF2LTJhNCA0IDAgMCAwLTQtNEg4YTQgNCAwIDAgMC00IDR2MiI+PC9wYXRoPjxjaXJjbGUgY3g9IjEyIiBjeT0iNyIgcj0iNCI+PC9jaXJjbGU+PC9zdmc+"> <span id="trix-user-name">Guest</span> </div> <div class="g-signin2" data-onsuccess="onSignIn" data-theme="dark"></div> </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><div id="cloud-notice"><b>NOTICE:</b> Due to browser security, scripts cannot be loaded directly. You can copy the code of the GreasyFork script you want to execute and then paste it in TriX Executor! Painsel is working on a better solution.</div><input type="text" class="search-input" placeholder="Search GreasyFork..." id="greasyfork-search" list="gf-suggestions"><datalist id="gf-suggestions"><option value="territorial.io"></option><option value="openfront.io"></option><option value="Discord"></option><option value="conqur.io"></option></datalist><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(){return`<div class="content-section" id="settings-content"></div>`}
function renderSettingsTab(){const s=document.getElementById('settings-content');if(!s)return;let updateHTML='';if(shouldShowUpdateReminder){updateHTML=`<div id="update-reminder"><p>You are using an older version of TriX Executor! Please update for the best experience.</p><button id="update-now-reminder" class="exec-btn success" style="width:100%">Update Now!</button></div>`}const st=settings;s.innerHTML=`<h2>Settings</h2><div class="settings-group"><label class="settings-label"><input type="checkbox" class="settings-checkbox" id="show-on-all-sites" ${st.showOnAllSites?"checked":""}>Show on All Sites</label></div><div class="settings-group"><label class="settings-label"><input type="checkbox" class="settings-checkbox" id="socket-toasts" ${st.socketToasts?"checked":""}>Socket Connection Toasts</label></div><div class="settings-group"><label class="settings-label"><input type="checkbox" class="settings-checkbox" id="block-propaganda" ${st.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" ${st.antiScam?"checked":""}>Anti-Scam Protection</label></div><div class="settings-group"><label class="settings-label"><input type="checkbox" class="settings-checkbox" id="show-ad" ${st.showAd?"checked":""}>Show TrixMusic Advertisement</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>${updateHTML}`}
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.innerHTML=`<img src="${GM_info.script.icon}" style="width:32px;height:32px;">`;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.closest('.minimized-icon'))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();else if(tabName==="settings")renderSettingsTab();}
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 res=await fetch(`https://greasyfork.org/en/scripts?q=${encodeURIComponent(q)}`),html=await res.text(),parser=new DOMParser,doc=parser.parseFromString(html,"text/html"),scriptElements=doc.querySelectorAll("li[data-script-id]");r.innerHTML="";if(!scriptElements.length){r.innerHTML='<div style="text-align:center;padding:20px;color:rgba(255,255,255,0.5)">No scripts found</div>';return}scriptElements.forEach(el=>{const id=el.dataset.scriptId,name=el.dataset.scriptName||"Unnamed",desc=el.querySelector(".script-description")?.innerText||"No description.",author=el.querySelector(".script-list-author a")?.innerText||"Unknown",installs=el.querySelector("dd.script-list-total-installs span")?.innerText.trim()||"0",codeUrl=`https://greasyfork.org/en/scripts/${id}/code.js`;const c=document.createElement("div");c.className="script-card";c.innerHTML=`<div class="card-title">${name}</div><div class="card-description">${desc}</div><div style="font-size:11px;color:rgba(255,255,255,0.6);margin-top:8px">By ${author} • ${installs} installs</div><button class="exec-btn success" data-url="${codeUrl}" data-name="${name}" style="margin-top:10px;padding:5px 10px;font-size:11px">Load Script</button>`;c.querySelector("button").onclick=e=>{showNotification("Loading script...");GM_xmlhttpRequest({method:'GET',url:e.target.dataset.url,onload:res=>{const code=res.responseText;switchTab("main");addScriptTab();const f=scriptTabs.find(t=>t.id===activeScriptTab);f.name=e.target.dataset.name;f.content=code;updateScriptTabs();showNotification("Script loaded into new tab!")},onerror:()=>showNotification("Failed to load script code.","error")})};r.appendChild(c)})}catch(e){console.error(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-user-profile'),b=document.querySelector('.g-signin2');if(d&&b){d.querySelector('#trix-user-pfp').src=i;d.querySelector('#trix-user-name').textContent=u;b.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"><img src="${GM_info.script.icon}" style="width:128px; height:128px; margin-bottom:1rem;"><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:4rem;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)};if(settings.showAd && !document.getElementById('trixmusic-toggle-button')){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">Remind me later</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){GM_setValue("showUpdateReminder",true);shouldShowUpdateReminder=true;renderSettingsTab();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;settings.showAd=document.getElementById("show-ad").checked;settings.showOnAllSites=document.getElementById("show-on-all-sites").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=>{try{const latest=JSON.parse(res.responseText).version;if(CURRENT_VERSION!==latest){if(!shouldShowUpdateReminder)showUpdateModal();else isUpdateAvailable=true;}else{GM_setValue("showUpdateReminder",false);shouldShowUpdateReminder=false}}catch(e){console.error("Failed to check for updates.")}}});
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);
}
})();