您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A powerful, multi-tabbed, persistent code execution and network logging environment. Stable WebSocket-based trigger and Shadow DOM for stealth.
当前为
// ==UserScript== // @name TriX Executor (BETA) for Territorial.io // @namespace https://gf.qytechs.cn/en/users/your-username // @version Beta-Lyra-2023.11.12 // @description A powerful, multi-tabbed, persistent code execution and network logging environment. Stable WebSocket-based trigger and Shadow DOM for stealth. // @author Painsel // @match *://territorial.io/* // @require https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js // @grant GM_getValue // @grant GM_setValue // @grant unsafeWindow // @run-at document-start // @license MIT // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzAwYWFmZiI+PHBhdGggZD0iTTE4LjM2IDIyLjA1bC00LjQyLTQuNDJDMTMuNDcgMTcuODcgMTMgMTguMTcgMTMgMTguNUMxMyAyMC45OSAxNS4wMSAyMyAxNy41IDIzaDMuNTRjLTIuNDUtMS40OC00LjQyLTMuNDUtNS45LTUuOTV6TTggMTNjMS42NiAwIDMuMTgtLjU5IDQuMzgtMS42MkwxMCAxMy41VjIybDIuNS0yLjVMMTggMTMuOTZjLjM1LS40OC42NS0xIC44Ny0xLjU1QzE4LjYxIDEzLjQxIDE4IDEyLjM0IDE4IDEyVjBoLTJ2MTJjMCAuMzQtLjAyLjY3LS4wNiAxLS4zMy4xOC0uNjguMy0xLjA0LjM3LTEuNzMgMC0zLjI3LS45My00LjE2LTIuMzZMNiAxMy42VjVINHY4eiIvPjwvc3ZnPg== // ==/UserScript== /* global Prism, unsafeWindow */ (function() { 'use strict'; // --- Promise-based Gatekeepers for Initialization --- let resolveWsPromise; const wsReadyPromise = new Promise(resolve => { resolveWsPromise = resolve; }); const domReadyPromise = new Promise(resolve => { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', resolve); } else { resolve(); } }); // --- WebSocket Proxy Setup --- if (!unsafeWindow.isTriXProxied) { unsafeWindow.OriginalWebSocket = unsafeWindow.WebSocket; let localLogPacketCallback = () => {}; unsafeWindow.WebSocket = function(url, protocols) { if (!url.includes('/s52/')) { return new unsafeWindow.OriginalWebSocket(url, protocols); } console.log(`[TriX] Intercepting WebSocket connection to: ${url}`); const ws = new unsafeWindow.OriginalWebSocket(url, protocols); const originalSend = ws.send.bind(ws); ws.send = function(data) { localLogPacketCallback('send', data); return originalSend(data); }; ws.addEventListener('message', (event) => { localLogPacketCallback('receive', event.data); }); ws.addEventListener('open', () => { console.log('[TriX] Game WebSocket connection opened. Firing ready signal.'); if (resolveWsPromise) { // This function will be called by the main script to set the real logger resolveWsPromise( (newCallback) => { localLogPacketCallback = newCallback; } ); resolveWsPromise = null; // Ensure it only fires once } }); return ws; }; unsafeWindow.isTriXProxied = true; } // --- Main script logic (runs after DOM is ready AND WebSocket is open) --- function initialize(setPacketLogger) { const shadowHost = document.createElement('div'); shadowHost.id = 'trix-host'; document.body.appendChild(shadowHost); const shadowRoot = shadowHost.attachShadow({ mode: 'open' }); const styleElement = document.createElement('style'); styleElement.textContent = ` /* PrismJS Theme */ code[class*="language-"],pre[class*="language-"]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;tab-size:4;}pre[class*="language-"]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*="language-"],pre[class*="language-"]{background:#2d2d2d}:not(pre)>code[class*="language-"]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment{color:#999}.token.punctuation{color:#ccc}.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc} /* --- TriX Executor UI --- */ @keyframes trix-fade-in { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } } @keyframes trix-slide-in { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } #trix-toggle-btn { position: fixed; top: 15px; right: 15px; z-index: 99999; width: 50px; height: 50px; background-color: rgba(30, 30, 34, 0.8); color: #00aaff; border: 2px solid #00aaff; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 24px; transition: all 0.3s ease; backdrop-filter: blur(5px); font-family: monospace; box-shadow: 0 0 10px rgba(0, 170, 255, 0.5); } #trix-toggle-btn:hover { transform: scale(1.1) rotate(15deg); box-shadow: 0 0 15px #00aaff; } #trix-container { position: fixed; top: 80px; right: 15px; width: 450px; min-height: 400px; z-index: 99998; color: #fff; border-radius: 10px; overflow: hidden; box-shadow: 0 5px 25px rgba(0,0,0,0.5); display: flex; flex-direction: column; backdrop-filter: blur(10px); animation: trix-slide-in 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; resize: both; } #trix-container.hidden { display: none; } #trix-container[data-theme='dark-knight'] { background-color: rgba(30, 30, 34, 0.9); border: 1px solid #444; } #trix-container[data-theme='arctic-light'] { background-color: rgba(240, 240, 255, 0.9); border: 1px solid #ccc; color: #111; } #trix-container[data-theme='crimson'] { background-color: rgba(43, 8, 8, 0.9); border: 1px solid #8B0000; color: #f1f1f1; } #trix-container[data-theme='arctic-light'] .trix-tab, #trix-container[data-theme='arctic-light'] .trix-status-bar, #trix-container[data-theme='arctic-light'] #trix-packet-log { background: #e0e0e8; } #trix-container[data-theme='arctic-light'] .trix-tab.active { background: #f0f0ff; color: #0055aa; } #trix-container[data-theme='arctic-light'] .trix-editor-area { background: #f0f0ff; border-color: #aaa; } #trix-container[data-theme='crimson'] .trix-tab.active { color: #ff8b8b; } #trix-header { padding: 10px 15px; cursor: move; user-select: none; font-weight: bold; font-size: 16px; display: flex; justify-content: space-between; align-items: center; } #trix-container[data-theme='dark-knight'] #trix-header { background-color: rgba(0,0,0,0.3); } #trix-container[data-theme='arctic-light'] #trix-header { background-color: rgba(0,0,0,0.1); } #trix-container[data-theme='crimson'] #trix-header { background-color: rgba(139, 0, 0, 0.5); } .trix-title-3d { color: #E0E4E8; font-family: 'Segoe UI', 'Roboto', sans-serif; font-weight: 900; font-size: 18px; letter-spacing: 1px; text-shadow: 0 0 2px rgba(255, 255, 255, 0.6), 0 0 10px #00aaff, 1px 1px 0 #0f121a, 2px 2px 0 #0f121a, 3px 3px 0 #0f121a, 4px 4px 5px rgba(0, 0, 0, 0.5); } .trix-version-tag { font-size: 10px; font-weight: 300; opacity: 0.7; margin-left: 8px; vertical-align: middle; } #trix-header-controls { display: flex; align-items: center; gap: 15px; } #trix-fps-display { font-size: 12px; font-weight: normal; opacity: 0.7; } #trix-close-btn { cursor: pointer; font-size: 20px; font-weight: bold; padding: 0 5px; } #trix-close-btn:hover { color: #ff5555; } #trix-content { padding: 0 15px 15px 15px; flex-grow: 1; display: flex; flex-direction: column; } .trix-tabs { display: flex; flex-wrap: wrap; border-bottom: 1px solid #555; } .trix-tab { background: #2a2a30; padding: 8px 12px; cursor: pointer; border-radius: 5px 5px 0 0; margin-right: 4px; position: relative; transition: background 0.2s; } .trix-tab:hover { background: #3a3a42; } .trix-tab.active { background: #1e1e22; font-weight: bold; color: #00aaff; } .trix-tab-name { padding-right: 15px; } .trix-tab-close { position: absolute; top: 50%; right: 5px; transform: translateY(-50%); font-size: 14px; opacity: 0.6; } .trix-tab-close:hover { opacity: 1; color: #ff5555; } #trix-new-tab-btn { background: none; border: none; color: #00aaff; font-size: 20px; cursor: pointer; padding: 5px 10px; } .trix-view { display: flex; flex-direction: column; flex-grow: 1; margin-top: 10px; } .trix-editor-area { position: relative; flex-grow: 1; margin-top: -1px; background: #2d2d2d; border: 1px solid #555; border-radius: 0 0 5px 5px; } .trix-editor-area textarea, .trix-editor-area pre { margin: 0; padding: 10px; font-family: 'Fira Code', 'Consolas', monospace; font-size: 14px; line-height: 1.5; white-space: pre; word-wrap: normal; width: 100%; height: 100%; box-sizing: border-box; position: absolute; top: 0; left: 0; overflow: auto; } .trix-editor-area textarea { z-index: 1; background: transparent; color: inherit; resize: none; border: none; outline: none; -webkit-text-fill-color: transparent; } .trix-editor-area pre { z-index: 0; pointer-events: none; } .trix-action-bar { display: flex; gap: 10px; margin-top: 10px; } .trix-button { background-color: #007bff; color: white; border: none; padding: 8px 12px; cursor: pointer; border-radius: 5px; transition: background-color 0.2s; flex-grow: 1; } #trix-execute-btn { background-color: #28a745; } .trix-status-bar { margin-top: 10px; padding: 5px; background: rgba(0,0,0,0.2); font-size: 12px; border-radius: 3px; min-height: 1em; } .trix-status-success { color: #28a745; } .trix-status-error { color: #dc3545; } #trix-packet-log-view { flex-grow: 1; display: flex; flex-direction: column; } #trix-packet-log { flex-grow: 1; background: #1e1e22; border: 1px solid #555; border-radius: 5px; padding: 10px; overflow-y: auto; font-family: monospace; font-size: 12px; white-space: pre-wrap; word-break: break-all; } .packet-item { padding: 2px 0; border-bottom: 1px solid #333; } .packet-send { color: #7ec699; } .packet-receive { color: #cc99cd; } .packet-meta { opacity: 0.6; font-size: 10px; margin-right: 10px; } #trix-settings-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); z-index: 100000; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(5px); } .trix-settings-content { background: #1e1e22; padding: 20px; border-radius: 8px; width: 300px; box-shadow: 0 0 20px rgba(0,0,0,0.5); animation: trix-fade-in 0.3s; } .trix-settings-content label { display: block; margin-top: 15px; } `; shadowRoot.appendChild(styleElement); const translations = { en: { toggleTitle: "Toggle TriX Executor (BETA)", headerTitle: "TriX Executor", versionPrefix: "(v{version})", fpsCounter: "FPS:", closeButton: "Close", executeButton: "Execute", clearButton: "Clear", statusBarReady: "Ready.", settingsButton: "Settings", settingsTitle: "Settings", themeLabel: "Theme:", languageLabel: "Language:", settingsCloseButton: "Close", newTabButton: "+", defaultTabName: "Welcome", defaultTabCode: "// Welcome to TriX Executor!\nconsole.log('Hello from TriX!');", packetLoggerTab: "Packet Logger", clearLogButton: "Clear Log", packetSend: "[SEND]", packetReceive: "[RECEIVE]", statusSuccess: "Success: Executed '{tabName}' at {time}", statusError: "Error: {errorMessage}", statusNothing: "Nothing to execute.", langName: "English" }, ru: { toggleTitle: "Переключить TriX Executor (БЕТА)", headerTitle: "TriX Executor", versionPrefix: "(v{version})", fpsCounter: "Кадров/с:", closeButton: "Закрыть", executeButton: "Выполнить", clearButton: "Очистить", statusBarReady: "Готово.", settingsButton: "Настройки", settingsTitle: "Настройки", themeLabel: "Тема:", languageLabel: "Язык:", settingsCloseButton: "Закрыть", newTabButton: "+", defaultTabName: "Приветствие", defaultTabCode: "// Добро пожаловать в TriX Executor!\nconsole.log('Привет от TriX!');", packetLoggerTab: "Логгер пакетов", clearLogButton: "Очистить лог", packetSend: "[ОТПР]", packetReceive: "[ПОЛУЧ]", statusSuccess: "Успех: Выполнено '{tabName}' в {time}", statusError: "Ошибка: {errorMessage}", statusNothing: "Нечего выполнять.", langName: "Русский" }, fr: { toggleTitle: "Basculer TriX Executor (BÊTA)", headerTitle: "TriX Executor", versionPrefix: "(v{version})", fpsCounter: "IPS:", closeButton: "Fermer", executeButton: "Exécuter", clearButton: "Effacer", statusBarReady: "Prêt.", settingsButton: "Paramètres", settingsTitle: "Paramètres", themeLabel: "Thème:", languageLabel: "Langue:", settingsCloseButton: "Fermer", newTabButton: "+", defaultTabName: "Bienvenue", defaultTabCode: "// Bienvenue dans TriX Executor !\nconsole.log('Bonjour de TriX !');", packetLoggerTab: "Logger de paquets", clearLogButton: "Effacer le log", packetSend: "[ENVOI]", packetReceive: "[RÉCEP]", statusSuccess: "Succès : '{tabName}' exécuté à {time}", statusError: "Erreur : {errorMessage}", statusNothing: "Rien à exécuter.", langName: "Français" }, pl: { toggleTitle: "Przełącz TriX Executor (BETA)", headerTitle: "TriX Executor", versionPrefix: "(v{version})", fpsCounter: "Kl/s:", closeButton: "Zamknij", executeButton: "Wykonaj", clearButton: "Wyczyść", statusBarReady: "Gotowy.", settingsButton: "Ustawienia", settingsTitle: "Ustawienia", themeLabel: "Motyw:", languageLabel: "Język:", settingsCloseButton: "Zamknij", newTabButton: "+", defaultTabName: "Witamy", defaultTabCode: "// Witaj w TriX Executor!\nconsole.log('Pozdrowienia od TriX!');", packetLoggerTab: "Logger pakietów", clearLogButton: "Wyczyść log", packetSend: "[WYŚLIJ]", packetReceive: "[ODBIÓR]", statusSuccess: "Sukces: Wykonano '{tabName}' o {time}", statusError: "Błąd: {errorMessage}", statusNothing: "Brak kodu do wykonania.", langName: "Polski" } }; let state = { tabs: [], activeTabId: null, settings: { theme: 'dark-knight', language: 'en', position: { top: '80px', left: 'auto', right: '15px' }, size: { width: '450px', height: '400px' } } }; const t = (key, replacements = {}) => { const lang = state.settings.language; let str = (translations[lang] && translations[lang][key]) || translations.en[key] || key; Object.entries(replacements).forEach(([placeholder, value]) => { str = str.replace(`{${placeholder}}`, value); }); return str; }; const $ = (selector, parent = shadowRoot) => parent.querySelector(selector); const debounce = (func, delay) => { let timeout; return (...args) => { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), delay); }; }; function loadState() { const savedState = GM_getValue('trixExecutorState'); if (savedState) { Object.assign(state, savedState); if (savedState.settings) Object.assign(state.settings, savedState.settings); } const defaultTabs = [ { id: 'logger', name: t('packetLoggerTab'), code: null, isPermanent: true }, { id: Date.now(), name: t('defaultTabName'), code: t('defaultTabCode') } ]; if (!state.tabs || state.tabs.length === 0) state.tabs = defaultTabs; if (!state.tabs.find(t => t.id === 'logger')) state.tabs.unshift(defaultTabs[0]); else state.tabs[0].name = t('packetLoggerTab'); if (!state.activeTabId || !state.tabs.find(t => t.id === state.activeTabId)) state.activeTabId = state.tabs[0].id; } const saveState = debounce(() => { const container = $('#trix-container'); if (container) { state.settings.position = { top: container.style.top, left: container.style.left, right: container.style.right, bottom: 'auto' }; state.settings.size = { width: container.style.width, height: container.style.height }; } GM_setValue('trixExecutorState', state); }, 500); let ui, editor; function reRenderUI() { const host = document.getElementById('trix-host'); if (host) host.remove(); initialize(setPacketLogger); } function createUI() { const containerHTML = `<div id="trix-toggle-btn" title="${t('toggleTitle')}">X</div><div id="trix-container" class="hidden"><div id="trix-header"><div><span class="trix-title-3d">${t('headerTitle')}</span><span class="trix-version-tag">${t('versionPrefix', {version: GM_info.script.version})}</span></div><div id="trix-header-controls"><span id="trix-fps-display"></span><span id="trix-close-btn" title="${t('closeButton')}">✖</span></div></div><div id="trix-content"></div><div id="trix-footer" style="padding:10px; text-align:center; background:rgba(0,0,0,0.2);"><button id="trix-settings-btn" class="trix-button" style="flex-grow:0; padding: 5px 15px;">${t('settingsButton')}</button></div></div>`; shadowRoot.innerHTML = containerHTML; return { toggleBtn: $('#trix-toggle-btn'), container: $('#trix-container') }; } function renderActiveView() { const content = $('#trix-content'); const activeTab = state.tabs.find(t => t.id === state.activeTabId); content.innerHTML = ''; const tabsHtml = `<div class="trix-tabs"></div>`; let viewHtml = ''; if (activeTab.id === 'logger') { viewHtml = `<div id="trix-packet-log-view" class="trix-view">${tabsHtml}<div id="trix-packet-log"></div><div class="trix-action-bar"><button id="trix-clear-log-btn" class="trix-button">${t('clearLogButton')}</button></div></div>`; } else { viewHtml = `<div id="trix-injector-container" class="trix-view">${tabsHtml}<div class="trix-editor-area"></div><div class="trix-action-bar"><button id="trix-execute-btn" class="trix-button">${t('executeButton')}</button><button id="trix-clear-btn" class="trix-button">${t('clearButton')}</button></div><div class="trix-status-bar">${t('statusBarReady')}</div></div>`; } content.innerHTML = viewHtml; renderTabs(); if ($('#trix-clear-log-btn')) $('#trix-clear-log-btn').addEventListener('click', () => $('#trix-packet-log').innerHTML = ''); } function renderTabs() { const tabsContainer = $('.trix-tabs'); if (!tabsContainer) return; tabsContainer.innerHTML = ''; state.tabs.forEach(tab => { const tabEl = document.createElement('div'); tabEl.className = 'trix-tab'; tabEl.dataset.tabId = tab.id; if (tab.id === state.activeTabId) tabEl.classList.add('active'); tabEl.innerHTML = `<span class="trix-tab-name">${tab.name}</span>` + (!tab.isPermanent ? `<span class="trix-tab-close">x</span>` : ''); tabsContainer.appendChild(tabEl); }); const newTabBtn = document.createElement('button'); newTabBtn.id = 'trix-new-tab-btn'; newTabBtn.textContent = t('newTabButton'); tabsContainer.appendChild(newTabBtn); if (state.tabs.find(t => t.id === state.activeTabId && t.id !== 'logger')) { renderEditor(); } } function renderEditor() { const editorArea = $('.trix-editor-area'); if (!editorArea) { editor = null; return; } const activeTab = state.tabs.find(t => t.id === state.activeTabId); if (!activeTab) { editorArea.innerHTML = ''; editor = null; return; } editorArea.innerHTML = `<textarea spellcheck="false" autocapitalize="off" autocomplete="off" autocorrect="off"></textarea><pre class="language-js"><code></code></pre>`; editor = { textarea: $('textarea', editorArea), pre: $('pre', editorArea), code: $('code', editorArea) }; editor.textarea.value = activeTab.code; highlightCode(activeTab.code); addEditorEventListeners(); } function highlightCode(code) { if (editor) editor.code.innerHTML = Prism.highlight(code + '\n', Prism.languages.javascript, 'javascript'); } function addEditorEventListeners() { if (!editor) return; editor.textarea.addEventListener('input', () => { const activeTab = state.tabs.find(t => t.id === state.activeTabId); if (activeTab) { activeTab.code = editor.textarea.value; highlightCode(activeTab.code); saveState(); } }); editor.textarea.addEventListener('scroll', () => { if (editor) { editor.pre.scrollTop = editor.textarea.scrollTop; editor.pre.scrollLeft = editor.textarea.scrollLeft; }}); editor.textarea.addEventListener('keydown', e => { if (e.key === 'Tab') { e.preventDefault(); const s = e.target.selectionStart, end = e.target.selectionEnd; e.target.value = e.target.value.substring(0, s) + ' ' + e.target.value.substring(end); e.target.selectionStart = e.target.selectionEnd = s + 2; editor.textarea.dispatchEvent(new Event('input')); } }); } function initEventListeners() { ui.toggleBtn.addEventListener('click', () => ui.container.classList.toggle('hidden')); $('#trix-close-btn').addEventListener('click', () => ui.container.classList.add('hidden')); initDraggable(ui.container, $('#trix-header')); ui.container.addEventListener('click', handleContainerClick); $('#trix-settings-btn').addEventListener('click', showSettingsModal); const resizeObserver = new ResizeObserver(debounce(saveState, 500)); resizeObserver.observe(ui.container); } function handleContainerClick(e) { const target = e.target; const tabEl = target.closest('.trix-tab'); if (tabEl && !target.classList.contains('trix-tab-close')) { const tabId = tabEl.dataset.tabId === 'logger' ? 'logger' : parseInt(tabEl.dataset.tabId, 10); if (tabId !== state.activeTabId) { state.activeTabId = tabId; renderActiveView(); saveState(); } } if (target.classList.contains('trix-tab-close')) { const tabId = parseInt(tabEl.dataset.tabId, 10); state.tabs = state.tabs.filter(t => t.id !== tabId); if (state.activeTabId === tabId) state.activeTabId = state.tabs[0].id; renderActiveView(); saveState(); } if (target.id === 'trix-new-tab-btn') { const newId = Date.now(), newName = `Script ${state.tabs.length}`; state.tabs.push({ id: newId, name: newName, code: `// ${newName}` }); state.activeTabId = newId; renderActiveView(); saveState(); } if (target.id === 'trix-execute-btn') executeScript(); if (target.id === 'trix-clear-btn') { const activeTab = state.tabs.find(t => t.id === state.activeTabId); if (activeTab) { activeTab.code = ''; renderEditor(); saveState(); } } } function initDraggable(container, handle) { let isDragging = false, offsetX, offsetY; handle.addEventListener('mousedown', e => { if (e.target.closest('#trix-header-controls') || e.target.closest('.trix-title-3d')) return; isDragging = true; offsetX = e.clientX - container.offsetLeft; offsetY = e.clientY - container.offsetTop; container.style.right = 'auto'; container.style.bottom = 'auto'; document.body.style.userSelect = 'none'; }); document.addEventListener('mousemove', e => { if (isDragging) { container.style.left = `${e.clientX - offsetX}px`; container.style.top = `${e.clientY - offsetY}px`; } }); document.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; document.body.style.userSelect = ''; saveState(); } }); } function executeScript() { const statusBar = $('.trix-status-bar'); if (!statusBar) return; const activeTab = state.tabs.find(t => t.id === state.activeTabId); if (!activeTab || !activeTab.code) { statusBar.textContent = t('statusNothing'); statusBar.className = 'trix-status-bar'; return; } try { new Function(activeTab.code)(); statusBar.textContent = t('statusSuccess', {tabName: activeTab.name, time: new Date().toLocaleTimeString()}); statusBar.className = 'trix-status-bar trix-status-success'; } catch (error) { console.error('TriX Executor Error:', error); statusBar.textContent = t('statusError', {errorMessage: error.message}); statusBar.className = 'trix-status-bar trix-status-error'; } } function showSettingsModal() { const modal = document.createElement('div'); modal.id = 'trix-settings-modal'; modal.innerHTML = `<div class="trix-settings-content"><h3>${t('settingsTitle')}</h3><label for="trix-theme-select">${t('themeLabel')}</label><select id="trix-theme-select"><option value="dark-knight">Dark Knight</option><option value="arctic-light">Arctic Light</option><option value="crimson">Crimson</option></select><label for="trix-lang-select">${t('languageLabel')}</label><select id="trix-lang-select"><option value="en">${translations.en.langName}</option><option value="ru">${translations.ru.langName}</option><option value="fr">${translations.fr.langName}</option><option value="pl">${translations.pl.langName}</option></select><button id="trix-settings-close">${t('settingsCloseButton')}</button></div>`; shadowRoot.appendChild(modal); $('#trix-theme-select').value = state.settings.theme; $('#trix-lang-select').value = state.settings.language; $('#trix-theme-select').addEventListener('change', e => { state.settings.theme = e.target.value; applySettings(); saveState(); }); $('#trix-lang-select').addEventListener('change', e => { state.settings.language = e.target.value; saveState(); modal.remove(); reRenderUI(); }); $('#trix-settings-close').addEventListener('click', () => modal.remove()); modal.addEventListener('click', e => { if (e.target.id === 'trix-settings-modal') modal.remove(); }); } function applySettings() { const container = $('#trix-container'); container.dataset.theme = state.settings.theme; Object.assign(container.style, state.settings.position, state.settings.size); } setPacketLogger(function(type, data) { const logContainer = $('#trix-packet-log'); if (!logContainer) return; const item = document.createElement('div'); item.className = `packet-item packet-${type}`; let formattedData = data; if (data instanceof ArrayBuffer) formattedData = `[ArrayBuffer, ${data.byteLength} bytes]`; else if (data instanceof Blob) formattedData = `[Blob, ${data.size} bytes, type: ${data.type}]`; const meta = type === 'send' ? t('packetSend') : t('packetReceive'); item.innerHTML = `<span class="packet-meta">${meta}</span> ${formattedData}`; logContainer.appendChild(item); logContainer.scrollTop = logContainer.scrollHeight; }); loadState(); ui = createUI(); applySettings(); renderActiveView(); initEventListeners(); let lastFrameTime = performance.now(), frameCount = 0; const fpsDisplay = $('#trix-fps-display'); function updateFPS(now) { frameCount++; if (now >= lastFrameTime + 1000) { fpsDisplay.textContent = `${t('fpsCounter')} ${frameCount}`; lastFrameTime = now; frameCount = 0; } requestAnimationFrame(updateFPS); } requestAnimationFrame(updateFPS); } Promise.all([domReadyPromise, wsReadyPromise]).then(([domEvent, setPacketLoggerFn]) => { console.log('[TriX] DOM is ready and WebSocket is open. Initializing UI.'); setTimeout(() => initialize(setPacketLoggerFn), 500); }).catch(error => { console.error('[TriX] Failed to initialize:', error); }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址