TriX Executor (REVAMP)

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

当前为 2025-11-08 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         TriX Executor (REVAMP)
// @namespace    https://greasyfork.org/en/users/COURTESYCOIL
// @version      5.0.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        unsafeWindow
// @license      MIT
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzRmNDZlNSI+PHBhdGggZD0iTTE4LjM2IDIyLjA1bC00LjQyLTQuNDJDMTMuNDcgMTcuODcgMTMgMTguMTcgMTMgMTguNUMxMyAyMC45OSAxNS4wMSAyMyAxNy41IDIzaDMuNTRjLTIuNDUtMS40OC00LjQyLTMuNDUtNS4LTUuOTV6TTggMTNjMS42NiAwIDMuMTgtLjU5IDQuMzgtMS42MkwxMCAxMy41VjIybDIuNS0yLjVMMTggMTMuOTZjLjM1LS40OC42NS0xIC44Ny0xLjU1QzE4LjYxIDEzLjQxIDE4IDEyLjM0IDE4IDEyVjBoLTJ2MTJjMCAuMzQtLjAyLjY3LS4wNiAxLS4zMy4xOC0uNjguMy0xLjA0LjM3LTEuNzMgMC0zLjI3LS45My00LjE2LTIuMzZMNiAxMy42VjVINXY4eiIvPjwvc3ZnPg==
// ==/UserScript==

(function () {
  "use strict";

  // --- 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 };
  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}`);
    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 & Music Player 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";
  const musicPlayerState = { audio: new Audio(), currentAlbum: "All Songs", currentSongIndex: -1, isPlaying: false, songs: [] };
  let isMusicInitialized = false;

  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: 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: #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.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 { position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: #fff; 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, .music-song-list::-webkit-scrollbar { width: 8px; }
    .trix-content::-webkit-scrollbar-track, .script-cards::-webkit-scrollbar-track, .packet-log::-webkit-scrollbar-track, .music-song-list::-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, .music-song-list::-webkit-scrollbar-thumb { background: rgba(79,70,229,0.6); border-radius: 4px; }
    
    /* --- Music Player Styles --- */
    .trixmusic-container { display: grid; grid-template-columns: 1fr 1.5fr; gap: 20px; height: 100%; }
    .music-playlist-panel, .music-player-panel { display: flex; flex-direction: column; background: rgba(0,0,0,0.2); border-radius: 8px; padding: 15px; }
    .music-player-panel { justify-content: center; align-items: center; }
    .music-playlist-header { display: grid; grid-template-columns: 1fr; gap: 10px; margin-bottom: 10px; }
    .music-song-list { overflow-y: auto; flex-grow: 1; }
    .music-song-item { padding: 10px; cursor: pointer; border-radius: 6px; transition: background-color 0.2s; }
    .music-song-item:hover, .music-song-item.playing { background-color: #2d2d44; }
    .music-song-title { display: block; font-weight: 600; color: #fff; }
    .music-song-artist { font-size: 12px; color: #a5b4fc; }
    .music-info { text-align: center; margin-bottom: 20px; }
    .music-title { font-size: 22px; font-weight: 700; display: block; color: #fff; }
    .music-artist { font-size: 14px; color: #a5b4fc; }
    .music-progress-container { width: 80%; background: rgba(0,0,0,0.3); border-radius: 5px; cursor: pointer; height: 8px; margin: 15px 0; }
    .music-progress-bar { background: #4f46e5; width: 0%; height: 100%; border-radius: 5px; }
    .music-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 15px 0; }
    .music-control-btn { background: none; border: none; color: #c7d2fe; cursor: pointer; font-size: 24px; opacity: 0.8; transition: all 0.2s; }
    .music-control-btn:hover { opacity: 1; color: #fff; transform: scale(1.1); }
    #music-play-pause-btn { font-size: 36px; }
    #music-volume-slider { width: 50%; accent-color: #4f46e5; }
  `;
  const styleSheet = document.createElement("style");
  styleSheet.textContent = styles;
  document.head.appendChild(styleSheet);
  
  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.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="trixmusic">🎵 TrixMusic</div>
                <div class="sidebar-item" data-tab="settings">⚙ Settings</div>
            </div>
            <div class="trix-content">
                ${createHomeContent()}
                ${createMainContent()}
                ${createNetworkContent()}
                ${createCloudContent()}
                ${createFilesContent()}
                ${createTrixMusicContent()}
                ${createSettingsContent()}
            </div>
        </div>
    `;
    document.body.appendChild(panel);
    return panel;
  }

  // --- MINIFIED HELPER FUNCTIONS ---
  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 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>`; }
  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 createTrixMusicContent() { return `<div class="content-section trixmusic-container" id="trixmusic-content"><div class="music-playlist-panel"><div class="music-playlist-header"><select class="settings-select" id="music-album-select"></select></div><input type="text" class="search-input" id="music-search-input" placeholder="Search songs..." style="margin-bottom: 10px;"><div class="music-song-list"></div></div><div class="music-player-panel"><div class="music-info"><span class="music-title">Select a Song</span><span class="music-artist">TrixMusic</span></div><div class="music-progress-container"><div class="music-progress-bar"></div></div><div class="music-controls"><button class="music-control-btn" id="music-prev-btn">⏮️</button><button class="music-control-btn" id="music-play-pause-btn">▶️</button><button class="music-control-btn" id="music-next-btn">⏭️</button></div><input type="range" id="music-volume-slider" min="0" max="1" step="0.01" value="0.5" title="Volume"></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"><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 (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" : ""} disabled>Auto Update Scripts (Future Feature)</label></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(); else if(tabName==="trixmusic" && !isMusicInitialized) { initTrixMusic(); isMusicInitialized=true; } }
  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,50).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||'No 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..."); try { 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!"); } catch (err) { showNotification("Failed to load script code.", "error"); } }; r.appendChild(c);});}catch(e){r.innerHTML='<div style="text-align:center; padding:20px; color:red;">Failed to fetch scripts</div>';}}
  unsafeWindow.onSignIn = function(googleUser) { const p=googleUser.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(callback) { const ls=document.createElement('div');ls.id='trix-loading-screen';const lm=["Enhancing experience, not breaking rules.","A dev tool for curious minds.","Building, not battling.","Scripts are your superpower."];ls.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">${lm[0]}</div></div>`;const sm=document.createElement('div');sm.id='trix-loading-settings-modal';sm.style.visibility='hidden';sm.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 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: #4f46e5; text-shadow: 2px 2px 0px #4338ca, 4px 4px 0px #312e81, 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, #4f46e5); border-radius: 10px; transition: width 0.1s linear; } .loading-message { font-style: italic; color: #9ca3af; min-height: 1.2em; margin-top: 1rem; }`;document.head.appendChild(s);document.body.append(ls,sm);setTimeout(()=>ls.classList.add("visible"),10);const p=ls.querySelector('.loading-bar-progress'),m=ls.querySelector('.loading-message');let r=0;const i=setInterval(()=>{r+=Math.random()*5;p.style.width=`${Math.min(r,100)}%`;if(r>25&&r<30)m.textContent=lm[1];if(r>55&&r<60)m.textContent=lm[2];if(r>85&&r<90)m.textContent=lm[3];if(r>=100){clearInterval(i);window.removeEventListener('keydown',f);ls.querySelector('.loading-content').classList.add('fade-out');setTimeout(()=>{ls.classList.remove('visible');setTimeout(()=>{ls.remove();sm.remove();s.remove();callback();},500);},500);}},100);const f=e=>{if(e.key==='F12'){e.preventDefault();clearInterval(i);sm.style.visibility='visible';sm.classList.add('visible');}};window.addEventListener('keydown',f);sm.querySelector('#trix-resume-loading').onclick=()=>{sm.classList.remove('visible');showLoadingScreen(callback);};}
  const albums = { "Phonk Mix": [{title:"Passo Bem Solto",artist:"ATLXS",url:"https://evzxirgylircpnblikrw.supabase.co/storage/v1/object/public/MOOZIK/ATLXS_-_PASSO_BEM_SOLTO_-_Slowed_@BaseNaija%20(2).mp3"}], "Rock Anthems": [{title:"Animal I Have Become",artist:"Three Days Grace",url:"https://www.dropbox.com/scl/fi/b3lgo2fkz85h0m3nklx3b/Skilet_-_Animal_I_Have_Become_-mp3.pm-1.mp3?rlkey=yok2i5r5in404ili6ozf776px&st=5xmvo7c5&dl=1"},{title:"It Has To Be This Way",artist:"Jamie Christopherson",url:"https://evzxirgylircpnblikrw.supabase.co/storage/v1/object/public/MOOZIK/Metal%20Gear%20Rising-%20Revengeance%20OST%20-%20It%20Has%20To%20Be%20This%20Way%20_Senator%20Battle_%20-%20Jamie%20Christopherson%20-%20SoundLoadMate.com.mp3"}], "Misc": [{title:"Honeypie",artist:"JAWNY",url:"https://evzxirgylircpnblikrw.supabase.co/storage/v1/object/public/MOOZIK/JAWNY_-_Honeypie_muzonov.net_(mp3.pm).mp3"}] };
  const renderSongList = (filter = "") => { const { currentAlbum } = musicPlayerState; musicPlayerState.songs = (currentAlbum === "All Songs") ? Object.values(albums).flat() : albums[currentAlbum]; const listEl = document.getElementById("music-song-list"); if (!listEl) return; listEl.innerHTML = ""; musicPlayerState.songs.filter(s => s.title.toLowerCase().includes(filter) || s.artist.toLowerCase().includes(filter)).forEach((song) => { const originalIndex = Object.values(albums).flat().findIndex(s => s.url === song.url); const item = document.createElement("div"); item.className = "music-song-item" + (originalIndex === musicPlayerState.currentSongIndex ? " playing" : ""); item.innerHTML = `<span class="music-song-title">${song.title}</span><span class="music-song-artist">${song.artist}</span>`; item.onclick = () => { loadSong(originalIndex); playSong(); }; listEl.appendChild(item); }); };
  const loadSong = (index) => { const allSongs = Object.values(albums).flat(); musicPlayerState.currentSongIndex = index; const song = allSongs[index]; musicPlayerState.audio.src = song.url; document.getElementById("music-title").textContent = song.title; document.getElementById("music-artist").textContent = song.artist; renderSongList(document.getElementById("music-search-input").value); };
  const playSong = () => { musicPlayerState.audio.play(); musicPlayerState.isPlaying = true; document.getElementById("music-play-pause-btn").textContent = "⏸️"; };
  const pauseSong = () => { musicPlayerState.audio.pause(); musicPlayerState.isPlaying = false; document.getElementById("music-play-pause-btn").textContent = "▶️"; };
  const nextSong = () => { let i = musicPlayerState.currentSongIndex + 1; if (i >= Object.values(albums).flat().length) i = 0; loadSong(i); playSong(); };
  const prevSong = () => { let i = musicPlayerState.currentSongIndex - 1; if (i < 0) i = Object.values(albums).flat().length - 1; loadSong(i); playSong(); };
  function initTrixMusic() { const p = musicPlayerState; p.audio.ontimeupdate = () => { const progress = (p.audio.currentTime / p.audio.duration) * 100; const pb = document.getElementById("music-progress-bar"); if(pb) pb.style.width = `${progress || 0}%`; }; p.audio.onended = nextSong; const pc = document.querySelector(".music-progress-container"); if(pc) pc.onclick = e => { if(p.audio.duration) p.audio.currentTime = (e.offsetX / e.target.clientWidth) * p.audio.duration; }; const as = document.getElementById("music-album-select"); if(as) as.innerHTML = "<option>All Songs</option>" + Object.keys(albums).map(a => `<option>${a}</option>`).join(''); renderSongList(); }
  
  // --- 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);
    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(); } if (e.target.matches('#music-play-pause-btn')) { musicPlayerState.isPlaying ? pauseSong() : playSong(); } if (e.target.matches('#music-next-btn')) nextSong(); if (e.target.matches('#music-prev-btn')) prevSong(); });
    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"; }); } if (e.target.matches('#music-search-input')) { renderSongList(e.target.value.toLowerCase()); } if (e.target.matches('#music-volume-slider')) { musicPlayerState.audio.volume = e.target.value; } });
    panel.addEventListener('change', e => { if (e.target.matches('#settings-content input')) { settings.antiScam = document.getElementById("anti-scam").checked; settings.blockPropaganda = document.getElementById("block-propaganda").checked; GM_setValue("settings", JSON.stringify(settings)); showNotification("Settings saved"); } if (e.target.matches('#music-album-select')) { musicPlayerState.currentAlbum = e.target.value; renderSongList(document.getElementById('music-search-input').value); } });
    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.toUpperCase() === "E") { e.preventDefault(); const p = document.querySelector('.trix-executor'); if (!p) 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; 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);
  }
})();