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 v5.0.1 (REVAMP)
// @namespace https://greasyfork.org/en/users/COURTESYCOIL
// @version 5.0.1
// @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 *://*/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_getClipboard
// @grant unsafeWindow
// @license MIT
// @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzRmNDZlNSI+PHBhdGggZD0iTTE4LjM2IDIyLjA1bC00LjQyLTQuNDJDMTMuNDcgMTcuODcgMTMgMTguMTcgMTMgMTguNUMxMyAyMC45OSAxNS4wMSAyMyAxNy.1IDIzaDMuNTRjLTIuNDUtMS40OC00LjQyLTMuNDUtNS4LTUuOTV6TTggMTNjMS42NiAwIDMuMTgtLjU5IDQuMzgtMS42MkwxMCAxMy41VjIybDIuNS0yLjVMMTggMTMuOTZjLjM1LS40OC42NS0xIC44Ny0xLjU1QzE4LjYxIDEzLjQxIDE4IDEyLjM0IDE4IDEyVjBoLTJ2MTJjMCAuMzQtLjAyLjY3LS4wNiAxLS4zMy4xOC0uNjguMy0xLjA0LjM3LTEuNzMgMC0zLjI3LS45My00LjE2LTIuMzZMNiAxMy42VjVINXY4eiIvPjwvc3ZnPg==
// ==/UserScript==
(function () {
"use strict";
// --- TriX Core: WebSocket Logging & Global State ---
let isLoggerSuspended = false;
let customWs = null;
const monitoredConnections = new Map();
// Callbacks for UI updates
let updateConnectionStatus = () => {};
let updatePingDisplay = () => {};
let logPacketCallback = () => {};
let onConnectionStateChange = () => {};
const OriginalWebSocket = unsafeWindow.WebSocket;
unsafeWindow.WebSocket = function(url, protocols) {
let isGameSocket = false;
try {
const isTerritorial = window.location.hostname.includes('territorial.io');
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, not a game socket */ }
if (!isGameSocket) return new OriginalWebSocket(url, protocols);
console.log(`[TriX] Intercepting WebSocket: ${url}`);
const ws = new OriginalWebSocket(url, protocols);
monitoredConnections.set(ws, { url, state: 'CONNECTING', log: [] });
onConnectionStateChange();
const originalSend = ws.send.bind(ws);
ws.send = function(data) {
logPacketCallback(ws, 'send', data);
return originalSend(data);
};
ws.addEventListener('message', event => {
logPacketCallback(ws, 'receive', event.data);
});
ws.addEventListener('open', () => {
const conn = monitoredConnections.get(ws);
if (conn) conn.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;
};
// --- Revamped UI: Global variables ---
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";
let settings = { theme: 'dark', antiScam: true, autoUpdate: true };
// Initialize storage
if (!GM_getValue("scripts", null)) {
GM_setValue("scripts", JSON.stringify([]));
}
const savedSettings = GM_getValue("settings", null);
if (savedSettings) {
settings = { ...settings, ...JSON.parse(savedSettings) };
} else {
GM_setValue("settings", JSON.stringify(settings));
}
// 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: #ffffff;
overflow: hidden; transition: all 0.3s ease; display: flex; flex-direction: column;
}
.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: white; }
.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; }
.content-section.active { display: block; }
/* Other styles remain mostly the same... */
.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: white; 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: white; 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.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: white; 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: block; margin-bottom: 8px; font-weight: 600; font-size: 13px; }
.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: white; font-size: 12px; outline: none; box-sizing: border-box; }
.notification { position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white; padding: 12px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); z-index: 1000000; font-size: 13px; font-weight: 600; opacity: 0; transform: translateX(100%); transition: all 0.3s ease; }
.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; }
.trix-content::-webkit-scrollbar-thumb:hover, .script-cards::-webkit-scrollbar-thumb:hover, .packet-log::-webkit-scrollbar-thumb:hover { background: rgba(79,70,229,0.8); }
`;
const styleSheet = document.createElement("style");
styleSheet.textContent = styles;
document.head.appendChild(styleSheet);
function showNotification(message, type = "success") { /* unchanged */ }
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.0.1 (REVAMP)</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;
}
// --- Content Section Creators ---
function createHomeContent() { /* unchanged */ }
function createMainContent() { /* unchanged */ }
function createCloudContent() { /* unchanged */ }
function createSettingsContent() { /* unchanged */ }
function createNetworkContent() { /* unchanged */ }
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 in your browser.</p>
<div class="script-cards" id="file-explorer-view"></div>
</div>`;
}
// --- Network View Renderers ---
function renderActiveNetworkView() { /* unchanged */ }
function renderPacketLog() { /* unchanged */ }
function renderStorageView() { /* unchanged */ }
// --- File Explorer ---
function loadFileExplorer() {
const container = document.getElementById("file-explorer-view");
if (!container) return;
container.innerHTML = "";
const gmScripts = JSON.parse(GM_getValue("scripts", "[]"));
const gmSettings = JSON.parse(GM_getValue("settings", "{}"));
const files = [
{ name: "scripts.json", type: "GM Storage", size: `${JSON.stringify(gmScripts).length} bytes` },
{ name: "settings.json", type: "GM Storage", size: `${JSON.stringify(gmSettings).length} bytes` },
...gmScripts.map(s => ({ name: s.name, type: 'Saved Script', size: `${s.code.length} chars` })),
];
files.forEach(file => {
const card = document.createElement("div");
card.className = "script-card";
card.style.cursor = "default";
card.innerHTML = `<div class="card-title">📄 ${file.name}</div>
<div class="card-description">${file.type} • ${file.size}</div>`;
container.appendChild(card);
});
}
// --- Panel Management & Draggability ---
function minimizePanel() { /* unchanged */ }
function restorePanel() { /* unchanged */ }
function createMinimizedIcon() { /* unchanged */ }
function closePanel() { /* unchanged */ }
// --- Tab Management ---
function switchTab(tabName) {
if (tabName === "files") { loadFileExplorer(); }
// ... rest of function is unchanged
}
function updateScriptTabs() { /* unchanged */ }
function switchScriptTab(tabId) { /* unchanged */ }
function addScriptTab() { /* unchanged */ }
function renameScriptTab(tabId) { /* unchanged */ }
// --- Script Logic ---
function executeScript(code) { /* unchanged */ }
function saveScript() { /* unchanged */ }
function loadSavedScripts() { /* unchanged */ }
// --- Cloud Logic ---
function searchGreasyFork(query) { /* unchanged */ }
// --- FULL IMPLEMENTATIONS OF UNCHANGED/HELPER FUNCTIONS ---
// (This section contains the full code for functions marked as unchanged for completeness)
function showNotification(message, type = "success") { const n = document.createElement("div"); n.className="notification"; n.textContent=message; if(type==="error"){n.style.background="linear-gradient(135deg, #ef4444 0%, #dc2626 100%)";} document.body.appendChild(n); setTimeout(()=>n.classList.add("show"),100); setTimeout(()=>{n.classList.remove("show"); setTimeout(()=>n.remove(),300)},3000); }
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 createSettingsContent() { const s = settings; return `<div class="content-section" id="settings-content"><h2>Settings</h2><div class="settings-group"><label class="settings-label">Theme</label><select class="settings-select" id="theme-select" disabled><option value="dark" ${s.theme === "dark" ? "selected" : ""}>Dark (Default)</option></select></div><div class="settings-group"><label class="settings-label"><input type="checkbox" class="settings-checkbox" id="anti-scam" ${s.antiScam ? "checked" : ""}>Anti-Scam Protection (confirm potentially harmful scripts)</label></div><div class="settings-group"><label class="settings-label"><input type="checkbox" class="settings-checkbox" id="auto-update" ${s.autoUpdate ? "checked" : ""}>Auto Update Scripts</label></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 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 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"); 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(query) { if(!query.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(query)}`),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,20).forEach(script=>{const c=document.createElement("div"); c.className="script-card"; c.innerHTML=`<div class="card-title">${script.name}</div><div class="card-description">${script.description}</div><div style="font-size:11px; color: rgba(255,255,255,0.6); margin-top:8px;">By ${script.users[0].name} • ${script.total_installs} installs</div><button class="exec-btn success" data-url="${script.code_url}" data-name="${script.name}" style="margin-top:10px; padding: 5px 10px; font-size:11px;">Load Script</button>`; c.querySelector("button").onclick=async e=>{showNotification("Loading script..."); const t=await fetch(e.target.dataset.url), a=await t.text(); switchTab("main"); addScriptTab(); const d=scriptTabs.find(t=>t.id===activeScriptTab); d.name=e.target.dataset.name; d.content=a; updateScriptTabs(); showNotification("Script loaded into new tab!");}; r.appendChild(c);});}catch(e){r.innerHTML='<div style="text-align:center; padding:20px; color:red;">Failed to fetch scripts</div>';}}
// --- GOOGLE SIGN-IN ---
unsafeWindow.onSignIn = function(googleUser) {
const profile = googleUser.getBasicProfile();
const userName = profile.getName();
const userImageUrl = profile.getImageUrl();
const profileDiv = document.querySelector('.trix-executor #trix-user-profile');
const signInButton = document.querySelector('.trix-executor .g-signin2');
const titleEl = document.querySelector('.trix-executor .trix-title');
if (profileDiv && signInButton) {
document.getElementById('trix-user-pfp').src = userImageUrl;
document.getElementById('trix-user-name').textContent = userName;
profileDiv.style.display = 'flex';
signInButton.style.display = 'none';
if (titleEl) titleEl.style.display = 'none'; // Hide default title
}
};
// --- LOADING SCREEN ---
function showLoadingScreen(callback) {
const themeVars = { accent: '#4f46e5', shadow1: '#4338ca', shadow2: '#312e81' };
const loadingStyle = document.createElement('style');
loadingStyle.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 0.5s ease-in-out; pointer-events: none; }
#trix-loading-screen.visible, #trix-loading-settings-modal.visible { opacity: 1; pointer-events: auto; }
.loading-content { opacity: 1; transition: opacity 1s ease-in-out; text-align: center; }
.loading-content.fade-out { opacity: 0; }
.loading-header { font-size: 5rem; font-weight: bold; color: ${themeVars.accent}; text-shadow: 2px 2px 0px ${themeVars.shadow1}, 4px 4px 0px ${themeVars.shadow2}, 6px 6px 10px rgba(0,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, ${themeVars.accent}); border-radius: 10px; transition: width 0.1s linear; }
.loading-message { font-style: italic; color: #9ca3af; min-height: 1.2em; margin-top: 1rem; }
#trix-loading-settings-modal .settings-grid { display: grid; grid-template-columns: 1fr 2fr; gap: 1rem 2rem; align-items: center; margin-top: 2rem; }
`;
document.head.appendChild(loadingStyle);
const loadingScreen = document.createElement('div');
loadingScreen.id = 'trix-loading-screen';
const loadingMessages = ["Enhancing experience, not breaking rules.", "A dev tool for curious minds.", "Building, not battling.", "Scripts are your superpower."];
loadingScreen.innerHTML = `<div class="loading-content">
<div class="loading-header">TriX Executor</div>
<div class="loading-bar-container"><div class="loading-bar-progress"></div></div>
<div class="loading-message">${loadingMessages[0]}</div>
</div>`;
const settingsModal = document.createElement('div');
settingsModal.id = 'trix-loading-settings-modal';
settingsModal.style.visibility = 'hidden';
settingsModal.innerHTML = `<div class="loading-content">
<div class="loading-header">Settings</div>
<div class="settings-grid">
<label class="settings-label">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>`;
document.body.append(loadingScreen, settingsModal);
setTimeout(() => loadingScreen.classList.add("visible"), 10);
const progressBar = loadingScreen.querySelector('.loading-bar-progress');
const messageText = loadingScreen.querySelector('.loading-message');
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 5;
progressBar.style.width = `${Math.min(progress, 100)}%`;
if (progress > 25 && progress < 30) messageText.textContent = loadingMessages[1];
if (progress > 55 && progress < 60) messageText.textContent = loadingMessages[2];
if (progress > 85 && progress < 90) messageText.textContent = loadingMessages[3];
if (progress >= 100) {
clearInterval(interval);
window.removeEventListener('keydown', f12Handler);
loadingScreen.querySelector('.loading-content').classList.add('fade-out');
setTimeout(() => {
loadingScreen.classList.remove('visible');
setTimeout(() => {
loadingScreen.remove(); settingsModal.remove(); loadingStyle.remove();
callback();
}, 500);
}, 500);
}
}, 100);
const f12Handler = e => { if (e.key === 'F12') { e.preventDefault(); clearInterval(interval); settingsModal.style.visibility = 'visible'; settingsModal.classList.add('visible'); } };
window.addEventListener('keydown', f12Handler);
settingsModal.querySelector('#trix-resume-loading').onclick = () => {
settingsModal.classList.remove('visible');
// Since there are no settings to save, we just resume loading. A real implementation would save settings here.
showLoadingScreen(callback); // Restart animation for simplicity
};
}
// --- Initialization ---
function init() {
const panel = createExecutorPanel();
const googleApiScript = document.createElement('script');
googleApiScript.src = "https://apis.google.com/js/platform.js";
googleApiScript.async = true;
googleApiScript.defer = true;
document.head.appendChild(googleApiScript);
// Event Delegation for the whole panel
panel.addEventListener('click', async e => {
if (e.target.matches('.minimize-btn')) minimizePanel();
if (e.target.matches('.maximize-btn')) { if (panel.style.width === "100vw") { Object.assign(panel.style, { width: "650px", height: "500px", top: "50px", left: "auto", right: "50px" }); } else { Object.assign(panel.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; const 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')) { panel.querySelectorAll('.network-tab').forEach(t => t.classList.remove('active')); e.target.classList.add('active'); activeNetworkTab = e.target.dataset.netTab; renderActiveNetworkView(); }
});
panel.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(); panel.querySelectorAll("#saved-scripts .script-card").forEach(c => { const t = c.querySelector(".card-title").textContent.toLowerCase(); c.style.display = t.includes(q) ? "block" : "none"; }); } });
panel.addEventListener('change', e => { if (e.target.matches('#settings-content input, #settings-content select')) { settings.antiScam = document.getElementById("anti-scam").checked; settings.autoUpdate = document.getElementById("auto-update").checked; GM_setValue("settings", JSON.stringify(settings)); showNotification("Settings saved"); } });
panel.addEventListener('keypress', e => { if (e.target.matches('#greasyfork-search') && e.key === 'Enter') { searchGreasyFork(e.target.value); } });
let isDragging = false, dragOffset = { x: 0, y: 0 };
panel.querySelector(".trix-header").onmousedown = (e) => { if (e.target.closest(".trix-btn, .g-signin2")) return; isDragging = true; const r = panel.getBoundingClientRect(); dragOffset = { x: e.clientX - r.left, y: e.clientY - r.top }; e.preventDefault(); };
document.onmousemove = (e) => { if (isDragging && !isMinimized) { const x = e.clientX - dragOffset.x, y = e.clientY - dragOffset.y; panel.style.left = Math.max(0, Math.min(x, window.innerWidth - panel.offsetWidth)) + "px"; panel.style.top = Math.max(0, Math.min(y, window.innerHeight - panel.offsetHeight)) + "px"; panel.style.right = "auto"; } };
document.onmouseup = () => { isDragging = false; };
document.onkeydown = (e) => { if (e.ctrlKey && e.shiftKey && e.key === "E") { e.preventDefault(); const p = document.querySelector('.trix-executor'); if (!p) init(); else if (isMinimized) restorePanel(); else minimizePanel(); } };
// Connect TriX callbacks
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;
const fpsDisplay = document.getElementById('trix-fps-display');
function updateFPS(now) { frameCount++; if (now >= lastFrameTime + 1000) { if(fpsDisplay) fpsDisplay.textContent = `FPS: ${frameCount}`; lastFrameTime = now; frameCount = 0; } requestAnimationFrame(updateFPS); }
if (fpsDisplay) requestAnimationFrame(updateFPS);
loadSavedScripts();
updateScriptTabs();
showNotification("TriX Executor loaded! Press Ctrl+Shift+E to toggle.");
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => showLoadingScreen(init));
} else {
showLoadingScreen(init);
}
})();