TriX Proxy Launcher

Launches and configures multiple CroxyProxy tabs. Hotkey 'B' now correctly submits the form by simulating an Enter key press while the input is still active.

// ==UserScript==
// @name         TriX Proxy Launcher
// @version      1.2.2
// @description  Launches and configures multiple CroxyProxy tabs. Hotkey 'B' now correctly submits the form by simulating an Enter key press while the input is still active.
// @author       painsel
// @license      MIT
// @match        https://www.croxyproxy.com/
// @grant        GM_addStyle
// @grant        GM_openInTab
// @namespace https://greasyfork.org/users/1490385
// ==/UserScript==

(function() {
    'use strict';

    // --- 1. Core Logic ---

    /**
     * Simulates human-like typing into an input field.
     * **FIX:** The onComplete callback is now executed BEFORE the element is blurred.
     */
    function simulateTyping(element, text, onComplete) {
        let i = 0;
        element.value = '';
        element.focus();
        function typeCharacter() {
            if (i < text.length) {
                element.value += text.charAt(i);
                element.dispatchEvent(new Event('input', { bubbles: true }));
                i++;
                setTimeout(typeCharacter, 50 + Math.random() * 100);
            } else {
                // Execute the callback FIRST, while the input is still focused.
                if (onComplete) onComplete();
                // THEN blur the element as a final cleanup step.
                element.blur();
            }
        }
        typeCharacter();
    }

    /**
     * Finds the required elements, types "territorial.io", and simulates pressing Enter.
     */
    function executeGoSequence() {
        const urlInput = document.getElementById('url');

        if (!urlInput) {
            console.error('[TriX Launcher] Could not find the URL input field.');
            return;
        }

        simulateTyping(urlInput, 'territorial.io', () => {
            // This callback is now executed while urlInput is still focused.
            const enterEvent = new KeyboardEvent('keydown', {
                key: 'Enter',
                code: 'Enter',
                keyCode: 13,
                which: 13,
                bubbles: true,
                cancelable: true,
            });
            urlInput.dispatchEvent(enterEvent);
        });
    }

    /**
     * Initializes the Launcher UI on the main CroxyProxy page.
     */
    function initializeLauncherUI() {
        const launcherCSS = `
            :root{--trix-bg:#1e1e1e;--trix-bg-light:#252526;--trix-header-bg:#333333;--trix-border:#3c3c3c;--trix-text:#d4d4d4;--trix-text-secondary:#cccccc;--trix-blue-accent:#007acc;--trix-button-bg:#0e639c;--trix-button-hover-bg:#1177bb;--trix-input-bg:#3c3c3c}
            #trix-launcher-container{position:fixed;top:20px;left:20px;width:280px;background-color:var(--trix-bg-light);border:1px solid var(--trix-border);border-radius:6px;color:var(--trix-text);font-family:'Consolas','Menlo','Courier New',monospace;font-size:14px;z-index:99999;box-shadow:0 5px 20px rgba(0,0,0,0.5);user-select:none;overflow:hidden}
            #trix-launcher-header{background-color:var(--trix-header-bg);padding:8px 12px;cursor:move;font-weight:bold;border-bottom:1px solid var(--trix-border)}
            #trix-launcher-body{padding:15px;display:flex;flex-direction:column;gap:15px}
            .trix-launcher-input-group{display:flex;flex-direction:column;gap:5px}
            .trix-launcher-input-group label{font-size:13px;color:var(--trix-text-secondary)}
            .trix-launcher-input-group input{background-color:var(--trix-input-bg);border:1px solid var(--trix-border);color:var(--trix-text);padding:8px;border-radius:4px;font-family:inherit;outline:none;transition:border-color .2s}
            .trix-launcher-input-group input:focus{border-color:var(--trix-blue-accent)}
            #trix-launch-btn{background-color:var(--trix-button-bg);color:white;border:none;padding:10px;border-radius:4px;font-family:inherit;font-size:14px;font-weight:bold;cursor:pointer;transition:background-color .2s}
            #trix-launch-btn:hover{background-color:var(--trix-button-hover-bg)}
            #trix-launch-btn:disabled{background-color:#5a5a5a;cursor:not-allowed}`;
        GM_addStyle(launcherCSS);

        const launcherHTML = `
            <div id="trix-launcher-container">
                <div id="trix-launcher-header">TriX Proxy Launcher</div>
                <div id="trix-launcher-body">
                    <div class="trix-launcher-input-group">
                        <label for="trix-tab-count">How many tabs to create?</label>
                        <input type="number" id="trix-tab-count" value="5" min="1" max="100">
                    </div>
                    <button id="trix-launch-btn">Launch Tabs</button>
                </div>
            </div>`;
        document.body.insertAdjacentHTML('beforeend', launcherHTML);

        const launchBtn = document.getElementById('trix-launch-btn');
        const tabCountInput = document.getElementById('trix-tab-count');

        launchBtn.addEventListener('click', () => {
            const count = parseInt(tabCountInput.value, 10);
            if (isNaN(count) || count < 1 || count > 100) {
                alert('Please enter a number between 1 and 100.');
                return;
            }

            launchBtn.disabled = true;
            launchBtn.textContent = 'Launching...';

            for (let i = 0; i < count; i++) {
                const newTab = GM_openInTab('https://www.croxyproxy.com/', { active: false, setParent: true });
                newTab.onclose = () => {};
                newTab.onload = () => {
                    newTab.sessionStorage.setItem('isTrixLaunchTarget', 'true');
                };
            }

            setTimeout(() => {
                launchBtn.disabled = false;
                launchBtn.textContent = 'Launch Tabs';
            }, 1000);
        });

        // Hotkey Listener
        document.addEventListener('keydown', (event) => {
            if (document.activeElement.tagName.toLowerCase().match(/input|textarea/)) {
                return;
            }

            if (event.key.toLowerCase() === 'b') {
                event.preventDefault();
                console.log('[TriX Launcher] "B" key pressed. Initiating go sequence.');
                executeGoSequence();
            }
        });

        // Draggable UI
        const container = document.getElementById('trix-launcher-container');
        const header = document.getElementById('trix-launcher-header');
        let isDragging = false, offsetX, offsetY;
        header.onmousedown = (e) => { isDragging = true; offsetX = e.clientX - container.offsetLeft; offsetY = e.clientY - container.offsetTop; document.onmousemove = (ev) => { if (isDragging) { container.style.left = `${ev.clientX - offsetX}px`; container.style.top = `${ev.clientY - offsetY}px`; } }; document.onmouseup = () => { isDragging = false; document.onmousemove = null; document.onmouseup = null; }; };
    }


    // --- 2. Execution Router ---
    if (sessionStorage.getItem('isTrixLaunchTarget') === 'true') {
        sessionStorage.removeItem('isTrixLaunchTarget');
        executeGoSequence();
    } else {
        initializeLauncherUI();
    }
})();