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)

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

Advertisement:

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

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();
        }
    });
})();