Live Script Sandbox (JS/CSS Injector & Debugger) 3

Yazdığınız JS/CSS kodunu anında enjekte eden, console.log'ları ve runtime hatalarını canlı panelde raporlayan premium simülatör. (v1.4: String Kaçış ve Görsel Kayma Korumalı Kesin Sürüm)

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Advertisement:

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

Advertisement:

// ==UserScript==
// @name         Live Script Sandbox (JS/CSS Injector & Debugger) 3
// @namespace    http://tampermonkey.net/
// @version      1.7
// @description  Yazdığınız JS/CSS kodunu anında enjekte eden, console.log'ları ve runtime hatalarını canlı panelde raporlayan premium simülatör. (v1.4: String Kaçış ve Görsel Kayma Korumalı Kesin Sürüm)
// @match        *://*/*
// @grant        none
// @icon         https://cdn-icons-png.flaticon.com/512/1216/1216641.png
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // String kaçış hatalarını engellemek için placeholder metinleri düz string haline getirildi
    const translations = {
        en: { titleLeft: "Sandbox Editor", titleRight: "Terminal Console", placeholder: "// Write JS here...\nconsole.log('Sandbox Greetings!');", cssPlaceholder: "/* Write CSS here... */\nbody { filter: invert(0); }", btnRun: "Run", btnClear: "Clear", successCss: "✨ CSS injected.", successJs: "✅ Execution zero errors.", emptyCode: "⚠ Write some code." },
        tr: { titleLeft: "Sandbox Editör", titleRight: "Terminal Konsolu", placeholder: "// JS yazın...\nconsole.log('Selamlar!');", cssPlaceholder: "/* CSS yazın... */\nbody { background: transparent; }", btnRun: "Çalıştır", btnClear: "Temizle", successCss: "✨ CSS enjekte edildi.", successJs: "✅ Kod yürüttü sıfır hata.", emptyCode: "⚠ Bir kod yazın." },
        de: { titleLeft: "Sandbox Editor", titleRight: "Terminal-Konsole", placeholder: "// JS schreiben...\nconsole.log('Grüße!');", cssPlaceholder: "/* CSS schreiben... */\nbody { filter: invert(0); }", btnRun: "Ausführen", btnClear: "Leeren", successCss: "✨ CSS injiziert.", successJs: "✅ Ausführung null Fehler.", emptyCode: "⚠ Code schreiben." }
    };

    let t = translations.tr;
    let mainHost = null;

    function toggleSandboxPanel() {
        if (mainHost) { mainHost.remove(); mainHost = null; } 
        else { createSandboxPanel(); }
    }

    function executeSandbox(rawCode, mode, outputTerminal) {
        if (!rawCode.trim()) {
            outputTerminal.innerHTML = `<span style="color: #94A3B8;">${t.emptyCode}</span>`;
            return;
        }

        if (mode === 'css') {
            let sandboxStyle = document.getElementById('tm-live-sandbox-style');
            if (!sandboxStyle) {
                sandboxStyle = document.createElement('style');
                sandboxStyle.id = 'tm-live-sandbox-style';
                document.head.appendChild(sandboxStyle);
            }
            sandboxStyle.textContent = rawCode;
            outputTerminal.innerHTML = `<span style="color: #10B981;">${t.successCss}</span>`;
        } else {
            outputTerminal.innerHTML = '';
            let logBuffer = [];
            
            const createLogLine = (type, args) => {
                const strArgs = args.map(arg => {
                    if (typeof arg === 'object') {
                        try { return JSON.stringify(arg); } catch(e) { return String(arg); }
                    }
                    return String(arg);
                }).join(' ');
                if (type === 'error') return `<div style="color: #F87171;">🛑 [CON-ERROR] ${strArgs}</div>`;
                if (type === 'warn') return `<div style="color: #FBBF24;">⚠️ [CON-WARN] ${strArgs}</div>`;
                return `<div style="color: #E2E8F0;">💬 [LOG] ${strArgs}</div>`;
            };

            const interceptedConsole = {
                log: (...args) => { logBuffer.push(createLogLine('log', args)); },
                error: (...args) => { logBuffer.push(createLogLine('error', args)); },
                warn: (...args) => { logBuffer.push(createLogLine('warn', args)); },
                info: (...args) => { logBuffer.push(createLogLine('log', args)); }
            };

            try {
                // Güvenli yürütme: Kod string izolasyonu sağlandı
                const sandboxExecution = new Function('console', '"use strict";\n' + rawCode);
                sandboxExecution(interceptedConsole);

                if (logBuffer.length > 0) {
                    outputTerminal.innerHTML = logBuffer.join('') + `<br><span style="color: #34D399; font-weight: bold;">${t.successJs}</span>`;
                } else {
                    outputTerminal.innerHTML = `<span style="color: #34D399; font-weight: bold;">${t.successJs}</span>`;
                }
            } catch (error) {
                let errorLine = "Bilinmiyor";
                if (error.stack) {
                    const match = error.stack.match(/<anonymous>:(\d+):(\d+)/);
                    if (match) errorLine = parseInt(match[1]) - 1;
                }
                outputTerminal.innerHTML = `
                    <div style="background: rgba(239, 68, 68, 0.1); border: 1px solid #EF4444; padding: 10px; border-radius: 6px; color: #F87171; font-family: monospace;">
                        🛑 SİMÜLASYON HATASI:<br>
                        <b>Tür:</b> ${error.name}<br>
                        <b>Mesaj:</b> ${error.message}<br>
                        <div><b>Tahmini Satır:</b> <span style="background: #EF4444; color: white; padding: 1px 5px; border-radius: 3px;">${errorLine}</span></div>
                    </div>
                `;
            }
        }
    }

    function createSandboxPanel() {
        if (mainHost) return;

        mainHost = document.createElement('div');
        mainHost.id = "script-sandbox-host";
        mainHost.style.position = "fixed";
        mainHost.style.zIndex = "999999999";
        mainHost.style.top = "0";
        mainHost.style.left = "0";
        
        const shadow = mainHost.attachShadow({ mode: 'open' });
        document.body.appendChild(mainHost);

        const overlay = document.createElement('div');
        overlay.className = "sandbox-overlay";

        overlay.innerHTML = `
            <div class="sandbox-container">
                <div class="panel editor-panel">
                    <div class="panel-header">
                        <span id="leftTitle">${t.titleLeft}</span>
                        <div class="header-actions">
                            <select id="modeSelect" class="premium-select mode-indicator">
                                <option value="js">🟨 JavaScript</option>
                                <option value="css">🟦 CSS Mode</option>
                            </select>
                            <select id="langSelect" class="premium-select">
                                <option value="en">🇬🇧 EN</option>
                                <option value="tr" selected>🇹🇷 TR</option>
                                <option value="de">🇩🇪 DE</option>
                            </select>
                            <button id="closeBtn" class="close-btn">✕</button>
                        </div>
                    </div>
                    <div class="code-area">
                        <pre class="line-numbers" id="leftLines"></pre>
                        <textarea id="inputCode" spellcheck="false" wrap="off"></textarea>
                    </div>
                </div>
                <div class="action-column">
                    <button id="runBtn" class="action-btn play-btn">
                        <svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
                    </button>
                    <button id="clearBtn" class="action-btn trash-btn">
                        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
                    </button>
                </div>
                <div class="panel terminal-panel">
                    <div class="panel-header">
                        <span id="rightTitle">${t.titleRight}</span>
                        <span class="terminal-badge">LIVE REACTION</span>
                    </div>
                    <div class="code-area terminal-bg">
                        <div id="outputTerminal"></div>
                    </div>
                </div>
            </div>
        `;

        const style = document.createElement('style');
        style.textContent = `
            .sandbox-overlay * { box-sizing: border-box; margin: 0; padding: 0; }
            .sandbox-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(9, 15, 29, 0.8); display: flex; align-items: center; justify-content: center; }
            .sandbox-container {
                width: 95vw; max-width: 1450px; height: 85vh; max-height: 85vh !important;
                background: #1E293B; border: 1px solid #334155; border-radius: 24px; padding: 20px;
                display: grid; grid-template-columns: 1fr 60px 1fr; gap: 16px; align-items: stretch; overflow: hidden !important;
            }
            .panel {
                background: #0F172A; border: 1px solid #1E293B; border-radius: 16px; padding: 16px;
                display: flex; flex-direction: column; gap: 14px; min-height: 0 !important; height: 100%; overflow: hidden;
            }
            .panel-header { display: flex; justify-content: space-between; align-items: center; height: 36px; flex-shrink: 0; }
            .panel-header span { font-size: 0.85rem; font-weight: 800; color: #94A3B8; text-transform: uppercase; letter-spacing: 0.8px; }
            .header-actions { display: flex; gap: 8px; align-items: center; }
            .premium-select, .close-btn { background: #1E293B; border: 1px solid #334155; border-radius: 8px; padding: 6px 12px; font-size: 0.75rem; font-weight: 600; cursor: pointer; color: #CBD5E1; outline: none; }
            .mode-indicator { background: #10B981; color: white; border: none; }
            .terminal-badge { font-size: 0.7rem !important; background: #334155; color: #38BDF8 !important; padding: 4px 10px; border-radius: 20px; font-weight: 700; }
            
            /* Görsel Kaymayı Önleyen Net Alan Kuralları */
            .code-area { 
                flex: 1; display: flex; background: #020617; border-radius: 12px; border: 1px solid #1E293B; 
                min-height: 0 !important; height: calc(100% - 50px); max-height: calc(100% - 50px); overflow: hidden !important; position: relative;
            }
            .terminal-bg { border-color: #334155; box-shadow: inset 0 4px 20px rgba(0,0,0,0.5); }
            
            .line-numbers {
                background: #090D1A; padding: 16px 8px; text-align: right; font-family: monospace; font-size: 0.9rem; color: #475569;
                min-width: 55px; max-width: 55px; line-height: 1.6; overflow: hidden !important; border-right: 1px solid #1E293B; height: 100%; white-space: pre;
            }
            textarea, #outputTerminal {
                font-family: monospace; font-size: 0.9rem; line-height: 1.6; border: none; background: transparent; outline: none; color: #E2E8F0;
            }
            textarea {
                flex: 1; padding: 16px; resize: none; overflow: auto !important; white-space: pre; height: 100%; width: calc(100% - 55px);
            }
            #outputTerminal { 
                width: 100%; height: 100%; padding: 16px; color: #38BDF8; overflow-y: auto !important; font-size: 0.85rem; white-space: pre-wrap; word-break: break-all;
            }
            
            .action-column { display: flex; flex-direction: column; gap: 16px; justify-content: center; align-self: center; height: fit-content; }
            .action-btn { color: white; border: none; width: 50px; height: 50px; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.25s; box-shadow: 0 8px 20px rgba(0,0,0,0.3); }
            .play-btn { background: #10B981; box-shadow: 0 6px 20px rgba(16, 185, 129, 0.4); }
            .play-btn:hover { background: #059669; transform: scale(1.1); }
            .trash-btn { background: #475569; }
            .trash-btn:hover { background: #EF4444; }
        `;

        shadow.appendChild(style);
        shadow.appendChild(overlay);

        const input = shadow.getElementById('inputCode');
        const outputTerminal = shadow.getElementById('outputTerminal');
        const leftLines = shadow.getElementById('leftLines');
        const runBtn = shadow.getElementById('runBtn');
        const clearBtn = shadow.getElementById('clearBtn');
        const closeBtn = shadow.getElementById('closeBtn');
        const modeSelect = shadow.getElementById('modeSelect');

        // Senkronize satır sayısı motoru
        const updateLineNumbers = () => {
            const lines = input.value.split('\n');
            const lineCount = lines.length;
            let nums = '';
            for (let i = 1; i <= lineCount; i++) nums += i + '\n';
            leftLines.textContent = nums;
        };

        input.addEventListener('input', updateLineNumbers);
        input.addEventListener('scroll', () => { leftLines.scrollTop = input.scrollTop; });

        runBtn.addEventListener('click', () => {
            executeSandbox(input.value, modeSelect.value, outputTerminal);
        });

        clearBtn.addEventListener('click', () => { outputTerminal.innerHTML = ''; });

        const closePanel = () => { if (mainHost) { mainHost.remove(); mainHost = null; } };
        closeBtn.addEventListener('click', closePanel);
        document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closePanel(); });
        
        input.value = t.placeholder;
        updateLineNumbers();
        input.focus();
    }

    document.addEventListener('keydown', (e) => {
        if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 's') {
            e.preventDefault(); toggleSandboxPanel();
        }
    });
})();