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)

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu betiği yüklemek için bir betik yöneticisi eklentisi yüklemeniz gerekecektir.

(Zaten bir betik yöneticim var, hadi yükleyelim!)

Advertisement:

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

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