Live Script Sandbox (JS/CSS Injector & Debugger)

Yazdığınız JS/CSS kodunu sayfayı yenilemeden anında sayfaya enjekte eden, console.log'ları ve runtime hatalarını canlı panelde raporlayan premium simülatör.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Advertisement:

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

Advertisement:

// ==UserScript==
// @name         Live Script Sandbox (JS/CSS Injector & Debugger)
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Yazdığınız JS/CSS kodunu sayfayı yenilemeden anında sayfaya enjekte eden, console.log'ları ve runtime hatalarını canlı panelde raporlayan premium simülatör.
// @match        *://*/*
// @grant        none
// @icon         https://cdn-icons-png.flaticon.com/512/1216/1216641.png
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    const translations = {
        en: {
            titleLeft: "Sandbox Editor",
            titleRight: "Terminal Console",
            placeholder: "// Write your JS code here...\\nconsole.log('Hello from Sandbox!');",
            cssPlaceholder: "/* Write your CSS here... */\\nbody { filter: invert(0); }",
            btnRun: "Run Script",
            btnClear: "Clear Logs",
            successCss: "✨ CSS successfully injected into document head.",
            successJs: "✅ Execution finished with zero uncaught errors.",
            emptyCode: "⚠ Terminal: Please write some code first."
        },
        tr: {
            titleLeft: "Sandbox Editör",
            titleRight: "Terminal Konsolu",
            placeholder: "// JS kodunuzu buraya yazın...\\nconsole.log('Sandbox\\'tan Selamlar!');",
            cssPlaceholder: "/* CSS kodunuzu buraya yazın... */\\nbody { filter: invert(0); }",
            btnRun: "Kodu Çalıştır",
            btnClear: "Konsolu Temizle",
            successCss: "✨ CSS, döküman head etiketine başarıyla enjekte edildi.",
            successJs: "✅ Kod yürütme sıfır hata ile tamamlandı.",
            emptyCode: "⚠ Terminal: Lütfen önce bir kod bloğu yazın."
        },
        de: {
            titleLeft: "Sandbox Editor",
            titleRight: "Terminal-Konsole",
            placeholder: "// Schreiben Sie hier Ihren JS-Code...\\nconsole.log('Hallo aus der Sandbox!');",
            cssPlaceholder: "/* Schreiben Sie hier Ihren CSS-Code... */\\nbody { filter: invert(0); }",
            btnRun: "Code Ausführen",
            btnClear: "Konsole Leeren",
            successCss: "✨ CSS erfolgreich in den Document Head injiziert.",
            successJs: "✅ Ausführung mit null unbehandelten Fehlern abgeschlossen.",
            emptyCode: "⚠ Terminal: Bitte schreiben Sie zuerst etwas Code."
        }
    };

    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; font-weight: bold;">${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; padding: 2px 0;">🛑 [CON-ERROR] ${strArgs}</div>`;
                if (type === 'warn') return `<div style="color: #FBBF24; padding: 2px 0;">⚠️ [CON-WARN] ${strArgs}</div>`;
                return `<div style="color: #E2E8F0; padding: 2px 0;">💬 [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 {
                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: 600;">${t.successJs}</span>`;
                } else {
                    outputTerminal.innerHTML = `<span style="color: #34D399; font-weight: 600;">${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.15); border: 1px solid #EF4444; padding: 12px; border-radius: 6px; color: #F87171; font-family: monospace;">
                        <b style="font-size: 0.95rem; display: block; margin-bottom: 6px;">🛑 SİMÜLASYON HATASI:</b>
                        <div style="margin-bottom: 4px;"><b>Tür:</b> ${error.name}</div>
                        <div style="margin-bottom: 4px;"><b>Mesaj:</b> ${error.message}</div>
                        <div><b>Tahmini Satır:</b> <span style="background: #EF4444; color: white; padding: 1px 6px; border-radius: 4px; font-weight: bold;">${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" title="ESC">✕</button>
                        </div>
                    </div>
                    <div class="code-area">
                        <div class="line-numbers" id="leftLines"></div>
                        <textarea id="inputCode" spellcheck="false"></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" tabindex="0"></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); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
                display: flex; align-items: center; justify-content: center;
                font-family: system-ui, -apple-system, sans-serif; padding: 24px;
            }
            .sandbox-container {
                width: 100vw; 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;
                box-shadow: 0 30px 70px -10px rgba(0, 0, 0, 0.7); 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% !important; max-height: 100% !important; overflow: hidden !important;
            }
            .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;
                transition: all 0.2s;
            }
            .mode-indicator { background: #10B981; color: white; border: none; }
            .premium-select:hover { border-color: #475569; background: #273549; }
            .close-btn:hover { background: #EF4444; color: white; border-color: #EF4444; }
            
            .terminal-badge { font-size: 0.7rem !important; background: #334155; color: #38BDF8 !important; padding: 4px 10px; border-radius: 20px; font-weight: 700; }

            .code-area { 
                flex: 1; display: flex; background: #020617; border-radius: 12px; 
                overflow: hidden !important; border: 1px solid #1E293B; min-height: 0 !important; height: 100%; max-height: 100%;
            }
            .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: 'Courier New', monospace;
                font-size: 0.85rem; color: #475569; user-select: none; min-width: 50px; white-space: pre; line-height: 1.6; 
                overflow: hidden !important; border-right: 1px solid #1E293B; height: 100%; max-height: 100%;
            }
            textarea, #outputTerminal {
                flex: 1; padding: 16px; font-family: 'Courier New', monospace; font-size: 0.9rem; line-height: 1.6;
                border: none; background: transparent; resize: none; outline: none; color: #E2E8F0;
                overflow: auto !important; white-space: pre; height: 100% !important; max-height: 100% !important; width: 100%;
            }
            #outputTerminal { white-space: pre-wrap; word-break: break-all; color: #38BDF8; overflow-y: auto !important; }
            
            .action-column { display: flex; flex-direction: column; gap: 16px; justify-content: center; align-items: center; flex-shrink: 0; }
            .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) rotate(90deg); }
            .trash-btn { background: #475569; }
            .trash-btn:hover { background: #EF4444; transform: scale(1.1); }
        `;

        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 langSelect = shadow.getElementById('langSelect');
        const modeSelect = shadow.getElementById('modeSelect');

        const updateLineNumbers = () => {
            const lines = input.value || '';
            const lineCount = lines.split('\n').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 = '';
        });

        modeSelect.addEventListener('change', (e) => {
            if (e.target.value === 'css') {
                e.target.style.background = '#0ea5e9';
                input.value = t.cssPlaceholder;
            } else {
                e.target.style.background = '#10B981';
                input.value = t.placeholder;
            }
            updateLineNumbers();
        });

        const closePanel = () => {
            if (mainHost) { mainHost.remove(); mainHost = null; }
            document.removeEventListener('keydown', escHandler);
        };

        closeBtn.addEventListener('click', closePanel);
        const escHandler = (e) => { if (e.key === 'Escape') closePanel(); };
        document.addEventListener('keydown', escHandler);
        
        langSelect.addEventListener('change', (e) => {
            t = translations[e.target.value];
            shadow.getElementById('leftTitle').textContent = t.titleLeft;
            shadow.getElementById('rightTitle').textContent = t.titleRight;
            runBtn.title = t.btnRun;
            clearBtn.title = t.btnClear;
            
            if (modeSelect.value === 'js') {
                input.placeholder = t.placeholder;
            } else {
                input.placeholder = t.cssPlaceholder;
            }
        });

        input.value = t.placeholder;
        updateLineNumbers();
        input.focus();
    }

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