{"id":84,"date":"2026-03-18T13:08:29","date_gmt":"2026-03-18T13:08:29","guid":{"rendered":"https:\/\/tools.sanepo.com\/?post_type=tool&#038;p=84"},"modified":"2026-03-18T13:08:30","modified_gmt":"2026-03-18T13:08:30","slug":"json-formatter-beautifier","status":"publish","type":"tool","link":"https:\/\/tools.sanepo.com\/es\/features\/json-formatter-beautifier\/","title":{"rendered":"Free Online JSON Formatter &amp; Beautifier \u2013 Auto-Fix &amp; Validate"},"content":{"rendered":"\n<style>\n    @import url('https:\/\/fonts.googleapis.com\/css2?family=Fira+Code:wght@400;500&display=swap');\n\n    \/* CSS Variables Default *\/\n    #wpnt-json-formatter {\n        \/* Warna border dipertegas untuk light mode *\/\n        --border-color: rgba(15, 23, 42, 0.18);\n        --accent-color: #4361ee;\n        --accent-hover: #3a56d4;\n        --transition: all 0.3s ease;\n        \n        \/* Tool Specific Variables *\/\n        --wpnt-success: #10b981;\n        --wpnt-danger: #ef4444;\n        --wpnt-warning: #f59e0b;\n        --wpnt-radius-lg: 16px;\n        --wpnt-radius-md: 12px;\n        --wpnt-radius-sm: 8px;\n\n        \/* Warna latar pane\/kotak yang sangat halus agar terpisah dari background web *\/\n        --pane-bg: rgba(15, 23, 42, 0.02);\n        --pane-header-bg: rgba(15, 23, 42, 0.05);\n\n        \/* Syntax Highlighting Colors (Default Light) *\/\n        --sh-key: #e11d48;\n        --sh-string: #16a34a;\n        --sh-number: #0284c7;\n        --sh-boolean: #9333ea;\n        --sh-null: #64748b;\n        \n        \/* --- KUNCI KEPATUHAN TEMA WP --- *\/\n        font-family: inherit;\n        color: inherit;\n        background: transparent;\n        \n        width: 100%;\n        margin: 30px auto;\n        border: 1px solid var(--border-color);\n        border-radius: var(--wpnt-radius-lg);\n        padding: 32px;\n        box-sizing: border-box;\n        line-height: 1.5;\n    }\n\n    #wpnt-json-formatter * {\n        box-sizing: border-box;\n        transition: var(--transition);\n    }\n\n    \/* Dark Mode Adaptability *\/\n    @media (prefers-color-scheme: dark) {\n        #wpnt-json-formatter {\n            \/* Penyesuaian border dan pane untuk dark mode *\/\n            --border-color: rgb(40 40 40 \/ 36%);\n            --pane-bg: rgba(255, 255, 255, 0.02);\n            --pane-header-bg: rgba(255, 255, 255, 0.06);\n\n            \/* Dark Mode Syntax Colors *\/\n            --sh-key: #fb7185;\n            --sh-string: #4ade80;\n            --sh-number: #38bdf8;\n            --sh-boolean: #d8b4fe;\n            --sh-null: #94a3b8;\n        }\n    }\n\n    \/* Header *\/\n    #wpnt-json-formatter .wpnt-header {\n        text-align: center;\n        margin-bottom: 24px;\n    }\n\n    #wpnt-json-formatter h3 {\n        margin: 0 0 8px 0;\n        font-size: 1.75rem;\n        font-weight: 700;\n        letter-spacing: -0.025em;\n    }\n\n    #wpnt-json-formatter p {\n        margin: 0;\n        font-size: 0.95rem;\n        opacity: 0.8;\n    }\n    \n    #wpnt-json-formatter .wpnt-brand-badge {\n        display: inline-block;\n        background: rgba(67, 97, 238, 0.1);\n        color: var(--accent-color);\n        border: 1px solid rgba(67, 97, 238, 0.2);\n        padding: 6px 12px;\n        border-radius: 20px;\n        font-size: 0.8rem;\n        font-weight: 600;\n        margin-bottom: 12px;\n    }\n\n    \/* Toolbar Actions *\/\n    #wpnt-json-formatter .wpnt-toolbar {\n        display: flex;\n        flex-wrap: wrap;\n        gap: 12px;\n        margin-bottom: 20px;\n        background: var(--pane-bg); \/* Background transparan halus *\/\n        padding: 16px;\n        border-radius: var(--wpnt-radius-md);\n        border: 1px solid var(--border-color);\n        align-items: center;\n        justify-content: space-between;\n    }\n\n    #wpnt-json-formatter .wpnt-toolbar-group {\n        display: flex;\n        flex-wrap: wrap;\n        gap: 8px;\n        align-items: center;\n    }\n\n    \/* Controls *\/\n    #wpnt-json-formatter select, #wpnt-json-formatter input[type=\"text\"] {\n        padding: 8px 12px;\n        border-radius: var(--wpnt-radius-sm);\n        border: 1px solid var(--border-color);\n        background-color: var(--pane-bg);\n        font-family: inherit;\n        font-size: 0.9rem;\n        color: inherit;\n        outline: none;\n    }\n\n    #wpnt-json-formatter select {\n        cursor: pointer;\n        appearance: none;\n        padding-right: 32px;\n        background-image: url(\"data:image\/svg+xml;charset=UTF-8,%3csvg xmlns='http:\/\/www.w3.org\/2000\/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c\/polyline%3e%3c\/svg%3e\");\n        background-repeat: no-repeat;\n        background-position: right 8px center;\n        background-size: 14px;\n    }\n\n    #wpnt-json-formatter select:focus, #wpnt-json-formatter input[type=\"text\"]:focus {\n        border-color: var(--accent-color);\n        box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.15);\n    }\n\n    #wpnt-json-formatter .wpnt-btn {\n        background: transparent;\n        color: inherit;\n        border: 1px solid var(--border-color);\n        padding: 8px 16px;\n        border-radius: var(--wpnt-radius-sm);\n        cursor: pointer;\n        font-weight: 600;\n        font-size: 0.9rem;\n        display: inline-flex;\n        align-items: center;\n        justify-content: center;\n        gap: 6px;\n    }\n\n    #wpnt-json-formatter .wpnt-btn:hover {\n        border-color: var(--accent-color);\n        background: rgba(67, 97, 238, 0.05);\n    }\n\n    #wpnt-json-formatter .wpnt-btn.primary {\n        background: var(--accent-color);\n        color: #fff;\n        border-color: var(--accent-color);\n    }\n\n    #wpnt-json-formatter .wpnt-btn.primary:hover {\n        background: var(--accent-hover);\n        transform: translateY(-1px);\n        box-shadow: 0 4px 12px rgba(67, 97, 238, 0.25);\n    }\n\n    #wpnt-json-formatter .wpnt-btn.magic {\n        background: linear-gradient(135deg, #8b5cf6, #d946ef);\n        color: white;\n        border: none;\n    }\n    \n    #wpnt-json-formatter .wpnt-btn.magic:hover {\n        opacity: 0.9;\n        box-shadow: 0 4px 12px rgba(217, 70, 239, 0.3);\n    }\n\n    \/* Editor Layout *\/\n    #wpnt-json-formatter .wpnt-editor-grid {\n        display: grid;\n        grid-template-columns: 1fr 1fr;\n        gap: 20px;\n        min-height: 450px;\n    }\n\n    #wpnt-json-formatter .wpnt-pane {\n        display: flex;\n        flex-direction: column;\n        background: var(--pane-bg); \/* Kotak input output sekarang punya bg tipis *\/\n        border: 1px solid var(--border-color);\n        border-radius: var(--wpnt-radius-md);\n        overflow: hidden;\n        position: relative;\n    }\n\n    #wpnt-json-formatter .wpnt-pane.dragover {\n        border: 2px dashed var(--accent-color);\n        background: rgba(67, 97, 238, 0.05);\n    }\n\n    #wpnt-json-formatter .wpnt-pane-header {\n        padding: 10px 16px;\n        background: var(--pane-header-bg); \/* Header pane sedikit lebih gelap dari isi pane *\/\n        border-bottom: 1px solid var(--border-color);\n        font-size: 0.85rem;\n        font-weight: 600;\n        color: var(--accent-color);\n        display: flex;\n        justify-content: space-between;\n        align-items: center;\n    }\n\n    #wpnt-json-formatter .pane-title {\n        display: flex;\n        align-items: center;\n        gap: 8px;\n        text-transform: uppercase;\n        letter-spacing: 0.5px;\n    }\n\n    \/* Small Actions in Pane Header *\/\n    #wpnt-json-formatter .pane-actions {\n        display: flex;\n        gap: 4px;\n    }\n\n    #wpnt-json-formatter .icon-btn {\n        background: none;\n        border: none;\n        color: inherit;\n        cursor: pointer;\n        padding: 4px;\n        border-radius: 4px;\n        opacity: 0.6;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n    }\n\n    #wpnt-json-formatter .icon-btn:hover {\n        opacity: 1;\n        background: var(--border-color);\n    }\n\n    \/* Filter Input *\/\n    #wpnt-json-formatter .wpnt-filter-wrapper {\n        position: relative;\n        display: flex;\n        align-items: center;\n    }\n    #wpnt-json-formatter .wpnt-filter-wrapper svg {\n        position: absolute;\n        left: 8px;\n        width: 14px;\n        height: 14px;\n        opacity: 0.5;\n    }\n    #wpnt-json-formatter .wpnt-filter-wrapper input {\n        padding: 4px 8px 4px 28px;\n        font-size: 0.8rem;\n        width: 180px;\n        border-radius: 12px;\n    }\n\n    \/* Badge *\/\n    #wpnt-json-formatter .wpnt-badge {\n        background: var(--border-color);\n        color: inherit;\n        padding: 2px 8px;\n        border-radius: 10px;\n        font-size: 0.7rem;\n        font-weight: 700;\n        opacity: 0.8;\n    }\n\n    #wpnt-json-formatter textarea, #wpnt-json-formatter pre {\n        flex: 1;\n        width: 100%;\n        margin: 0;\n        padding: 16px;\n        border: none;\n        background: transparent;\n        color: inherit;\n        font-family: 'Fira Code', monospace;\n        font-size: 0.85rem;\n        line-height: 1.6;\n        resize: none;\n        outline: none;\n        overflow: auto;\n    }\n\n    #wpnt-json-formatter textarea.error-border {\n        box-shadow: inset 0 0 0 2px var(--wpnt-danger);\n    }\n\n    #wpnt-json-formatter textarea::placeholder {\n        color: inherit;\n        opacity: 0.4;\n    }\n\n    \/* Syntax Highlighting Classes *\/\n    #wpnt-json-formatter .sh-key { color: var(--sh-key); font-weight: 500; }\n    #wpnt-json-formatter .sh-string { color: var(--sh-string); }\n    #wpnt-json-formatter .sh-number { color: var(--sh-number); }\n    #wpnt-json-formatter .sh-boolean { color: var(--sh-boolean); font-weight: 600; }\n    #wpnt-json-formatter .sh-null { color: var(--sh-null); font-weight: 600; font-style: italic; }\n\n    \/* Status Bar *\/\n    #wpnt-json-formatter .wpnt-status {\n        margin-top: 16px;\n        padding: 12px 16px;\n        border-radius: var(--wpnt-radius-md);\n        font-size: 0.9rem;\n        font-weight: 500;\n        display: none;\n        align-items: center;\n        gap: 8px;\n    }\n\n    #wpnt-json-formatter .wpnt-status.success {\n        display: flex;\n        background: rgba(16, 185, 129, 0.1);\n        color: var(--wpnt-success);\n        border: 1px solid rgba(16, 185, 129, 0.2);\n    }\n\n    #wpnt-json-formatter .wpnt-status.error {\n        display: flex;\n        background: rgba(239, 68, 68, 0.1);\n        color: var(--wpnt-danger);\n        border: 1px solid rgba(239, 68, 68, 0.2);\n    }\n\n    \/* Icons *\/\n    #wpnt-json-formatter svg {\n        width: 16px;\n        height: 16px;\n        stroke-width: 2.5;\n    }\n\n    \/* Responsiveness *\/\n    @media (max-width: 800px) {\n        #wpnt-json-formatter {\n            padding: 20px 16px;\n        }\n        #wpnt-json-formatter .wpnt-editor-grid {\n            grid-template-columns: 1fr;\n        }\n        #wpnt-json-formatter .wpnt-toolbar {\n            flex-direction: column;\n            align-items: stretch;\n        }\n        #wpnt-json-formatter .wpnt-toolbar-group {\n            justify-content: space-between;\n        }\n        #wpnt-json-formatter .wpnt-filter-wrapper input {\n            width: 140px;\n        }\n    }\n<\/style>\n\n<div id=\"wpnt-json-formatter\">\n    <div class=\"wpnt-header\">\n        <span class=\"wpnt-brand-badge\">Sanepo Tools<\/span>\n        <h3>JSON Formatter &#038; Beautifier<\/h3>\n        <p>Clean, format, filter, and validate your JSON data securely in the browser.<\/p>\n    <\/div>\n\n    <div class=\"wpnt-toolbar\">\n        <div class=\"wpnt-toolbar-group\">\n            <select id=\"wpnt-indent-select\" aria-label=\"Indentation Space\">\n                <option value=\"2\">2 Spaces<\/option>\n                <option value=\"4\">4 Spaces<\/option>\n                <option value=\"tab\">Tabs<\/option>\n            <\/select>\n            <button class=\"wpnt-btn\" id=\"wpnt-btn-sort\">\n                <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 6h18\"\/><path d=\"M7 12h10\"\/><path d=\"M10 18h4\"\/><\/svg>\n                Sort Keys\n            <\/button>\n            <button class=\"wpnt-btn magic\" id=\"wpnt-btn-fix\" title=\"Auto-fix common JSON errors (trailing commas, quotes)\">\n                <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z\"\/><\/svg>\n                Auto-Fix\n            <\/button>\n        <\/div>\n        <div class=\"wpnt-toolbar-group\">\n            <button class=\"wpnt-btn\" id=\"wpnt-btn-minify\">Minify<\/button>\n            <button class=\"wpnt-btn primary\" id=\"wpnt-btn-beautify\">Beautify<\/button>\n            <button class=\"wpnt-btn\" id=\"wpnt-btn-copy\">\n                <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"\/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"\/><\/svg>\n                Copy\n            <\/button>\n            <button class=\"wpnt-btn\" id=\"wpnt-btn-download\">\n                <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"\/><polyline points=\"7 10 12 15 17 10\"\/><line x1=\"12\" x2=\"12\" y1=\"15\" y2=\"3\"\/><\/svg>\n            <\/button>\n        <\/div>\n    <\/div>\n\n    <div class=\"wpnt-editor-grid\">\n        <!-- Input Pane -->\n        <div class=\"wpnt-pane\" id=\"wpnt-pane-input\">\n            <div class=\"wpnt-pane-header\">\n                <span class=\"pane-title\">\n                    Input \n                    <span id=\"wpnt-input-size\" class=\"wpnt-badge\">0 B<\/span>\n                <\/span>\n                <div class=\"pane-actions\">\n                    <button class=\"icon-btn\" id=\"wpnt-btn-clear\" title=\"Clear Input\">\n                        <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 6h18\"\/><path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\"\/><path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\"\/><\/svg>\n                    <\/button>\n                    <button class=\"icon-btn\" id=\"wpnt-btn-upload\" title=\"Upload JSON File\">\n                        <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"\/><polyline points=\"17 8 12 3 7 8\"\/><line x1=\"12\" x2=\"12\" y1=\"3\" y2=\"15\"\/><\/svg>\n                    <\/button>\n                    <input type=\"file\" id=\"wpnt-file-input\" accept=\".json,application\/json\" style=\"display: none;\">\n                <\/div>\n            <\/div>\n            <textarea id=\"wpnt-input\" placeholder=\"Paste your raw JSON here... or Drop a .json file\"><\/textarea>\n        <\/div>\n\n        <!-- Output Pane -->\n        <div class=\"wpnt-pane\">\n            <div class=\"wpnt-pane-header\">\n                <span class=\"pane-title\">Output<\/span>\n                <div class=\"wpnt-filter-wrapper\" title=\"Extract data using dot notation (e.g., users.0.name)\">\n                    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"11\" cy=\"11\" r=\"8\"\/><path d=\"m21 21-4.3-4.3\"\/><\/svg>\n                    <input type=\"text\" id=\"wpnt-filter\" placeholder=\"Filter path...\">\n                <\/div>\n            <\/div>\n            <pre><code id=\"wpnt-output\"><\/code><\/pre>\n        <\/div>\n    <\/div>\n\n    <div id=\"wpnt-status-bar\" class=\"wpnt-status\"><\/div>\n<\/div>\n\n<script>\n\/**\n * WP-NanoTech: Sanepo Tools JSON Formatter Engine\n * Zero Dependencies. 100% Client-Side. Advanced Features.\n *\/\n(() => {\n    \/\/ 1. Elemen DOM\n    const inputArea = document.getElementById('wpnt-input');\n    const outputArea = document.getElementById('wpnt-output');\n    const inputPane = document.getElementById('wpnt-pane-input');\n    const filterInput = document.getElementById('wpnt-filter');\n    const fileInput = document.getElementById('wpnt-file-input');\n    const sizeBadge = document.getElementById('wpnt-input-size');\n    const statusBar = document.getElementById('wpnt-status-bar');\n\n    \/\/ Buttons\n    const btnBeautify = document.getElementById('wpnt-btn-beautify');\n    const btnMinify = document.getElementById('wpnt-btn-minify');\n    const btnSort = document.getElementById('wpnt-btn-sort');\n    const btnFix = document.getElementById('wpnt-btn-fix');\n    const btnCopy = document.getElementById('wpnt-btn-copy');\n    const btnDownload = document.getElementById('wpnt-btn-download');\n    const btnClear = document.getElementById('wpnt-btn-clear');\n    const btnUpload = document.getElementById('wpnt-btn-upload');\n    const indentSelect = document.getElementById('wpnt-indent-select');\n\n    \/\/ State Internal\n    let currentRawData = \"\";\n    let lastParsedObj = null;\n    let isMinified = false;\n\n    \/\/ 2. Fungsi Utilitas (Format Size & Status)\n    const formatBytes = (bytes, decimals = 2) => {\n        if (bytes === 0) return '0 B';\n        const k = 1024, dm = decimals < 0 ? 0 : decimals;\n        const sizes = ['B', 'KB', 'MB', 'GB'];\n        const i = Math.floor(Math.log(bytes) \/ Math.log(k));\n        return parseFloat((bytes \/ Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];\n    };\n\n    const updateSizeIndicator = (text) => {\n        const size = new Blob([text]).size;\n        sizeBadge.innerText = formatBytes(size);\n    };\n\n    const showStatus = (msg, type = 'success') => {\n        statusBar.className = `wpnt-status ${type}`;\n        statusBar.innerHTML = type === 'success' \n            ? `<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\"\/><polyline points=\"22 4 12 14.01 9 11.01\"\/><\/svg> ${msg}`\n            : `<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"\/><line x1=\"12\" x2=\"12\" y1=\"8\" y2=\"12\"\/><line x1=\"12\" x2=\"12.01\" y1=\"16\" y2=\"16\"\/><\/svg> ${msg}`;\n        \n        if (type === 'success') {\n            setTimeout(() => {\n                if(statusBar.classList.contains('success')) statusBar.style.display = 'none';\n            }, 3000);\n        }\n    };\n\n    \/\/ 3. Syntax Highlighting (Regex)\n    const syntaxHighlight = (jsonStr) => {\n        if(isMinified) return jsonStr.replace(\/&\/g, '&amp;').replace(\/<\/g, '&lt;').replace(\/>\/g, '&gt;');\n\n        let html = jsonStr.replace(\/&\/g, '&amp;').replace(\/<\/g, '&lt;').replace(\/>\/g, '&gt;');\n        return html.replace(\/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)\/g, (match) => {\n            let cls = 'sh-number';\n            if (\/^\"\/.test(match)) {\n                if (\/:$\/.test(match)) {\n                    cls = 'sh-key';\n                } else {\n                    cls = 'sh-string';\n                }\n            } else if (\/true|false\/.test(match)) {\n                cls = 'sh-boolean';\n            } else if (\/null\/.test(match)) {\n                cls = 'sh-null';\n            }\n            return `<span class=\"${cls}\">${match}<\/span>`;\n        });\n    };\n\n    \/\/ 4. Advanced Features: Object Sorter & Path Filter\n    const sortObjectKeys = (obj) => {\n        if (typeof obj !== 'object' || obj === null) return obj;\n        if (Array.isArray(obj)) return obj.map(sortObjectKeys);\n        \n        return Object.keys(obj).sort().reduce((result, key) => {\n            result[key] = sortObjectKeys(obj[key]);\n            return result;\n        }, {});\n    };\n\n    \/\/ Ekstrak data berdasarkan path (contoh: \"users.0.name\" atau \"data.id\")\n    const getByPath = (obj, path) => {\n        if (!path) return obj;\n        const properties = path.split('.');\n        return properties.reduce((prev, curr) => prev && prev[curr], obj);\n    };\n\n    \/\/ 5. Core Processing Logic\n    const processJSON = (action = 'beautify', customData = null) => {\n        const rawText = inputArea.value.trim();\n        updateSizeIndicator(rawText);\n\n        if (!rawText) {\n            outputArea.innerHTML = '';\n            statusBar.style.display = 'none';\n            inputArea.classList.remove('error-border');\n            lastParsedObj = null;\n            return;\n        }\n\n        try {\n            \/\/ Jika ada customData (seperti dari Sort), gunakan itu. Jika tidak, parse input.\n            let parsed = customData || JSON.parse(rawText);\n            lastParsedObj = parsed; \/\/ Simpan untuk fungsi filter\n            inputArea.classList.remove('error-border');\n\n            \/\/ Cek jika filter path aktif\n            const filterPath = filterInput.value.trim();\n            if (filterPath) {\n                parsed = getByPath(parsed, filterPath);\n                if (parsed === undefined) {\n                    outputArea.innerHTML = '<span style=\"color:var(--sh-null);\">\/* Path not found *\/<\/span>';\n                    return;\n                }\n            }\n\n            const space = action === 'minify' ? 0 : (indentSelect.value === 'tab' ? '\\t' : parseInt(indentSelect.value, 10));\n            isMinified = action === 'minify';\n            \n            const formatted = JSON.stringify(parsed, null, space);\n            currentRawData = formatted;\n            outputArea.innerHTML = syntaxHighlight(formatted);\n\n            if(action === 'beautify' && !customData && !filterPath) showStatus('JSON formatted successfully!');\n            if(action === 'minify') showStatus('JSON minified successfully!');\n\n        } catch (error) {\n            outputArea.innerHTML = '';\n            currentRawData = '';\n            inputArea.classList.add('error-border');\n            showStatus(`Error: ${error.message}`, 'error');\n        }\n    };\n\n    \/\/ 6. Fitur Brilian: Auto Fixer\n    const magicFixJSON = () => {\n        let text = inputArea.value;\n        if (!text) return;\n\n        try {\n            JSON.parse(text);\n            showStatus('JSON is already valid.', 'success');\n            processJSON('beautify');\n        } catch (e) {\n            let fixed = text\n                .replace(\/,\\s*([\\]}])\/g, '$1') \/\/ Hapus trailing commas\n                .replace(\/(['\"])?([a-zA-Z0-9_]+)(['\"])?\\s*:\/g, '\"$2\": ') \/\/ Fix missing\/single quotes di key\n                .replace(\/'([^']*)'\/g, '\"$1\"'); \/\/ Fix single quote string\n\n            inputArea.value = fixed;\n            \n            try {\n                JSON.parse(fixed);\n                showStatus('Auto-Fix Applied! Formatting JSON...', 'success');\n                processJSON('beautify');\n            } catch (err) {\n                showStatus('Auto-Fix attempted, but JSON is still invalid. Check syntax manually.', 'error');\n            }\n        }\n    };\n\n    \/\/ 7. Input\/Output Handlers (Download, Copy, Upload, Drag&Drop)\n    const handleFileLoad = (file) => {\n        if (!file || file.type !== \"application\/json\" && !file.name.endsWith('.json')) {\n            showStatus('Please upload a valid .json file', 'error');\n            return;\n        }\n        const reader = new FileReader();\n        reader.onload = (e) => {\n            inputArea.value = e.target.result;\n            processJSON('beautify');\n            showStatus('File loaded successfully!');\n        };\n        reader.readAsText(file);\n    };\n\n    \/\/ Event Listeners - Drag and Drop\n    inputPane.addEventListener('dragover', (e) => {\n        e.preventDefault();\n        inputPane.classList.add('dragover');\n    });\n    inputPane.addEventListener('dragleave', () => inputPane.classList.remove('dragover'));\n    inputPane.addEventListener('drop', (e) => {\n        e.preventDefault();\n        inputPane.classList.remove('dragover');\n        if (e.dataTransfer.files.length) handleFileLoad(e.dataTransfer.files[0]);\n    });\n\n    \/\/ File Upload via Button\n    btnUpload.addEventListener('click', () => fileInput.click());\n    fileInput.addEventListener('change', (e) => {\n        if (e.target.files.length) handleFileLoad(e.target.files[0]);\n        e.target.value = ''; \/\/ Reset input\n    });\n\n    \/\/ Action Buttons\n    btnClear.addEventListener('click', () => {\n        inputArea.value = '';\n        filterInput.value = '';\n        processJSON();\n    });\n\n    btnDownload.addEventListener('click', () => {\n        if (!currentRawData) return;\n        const blob = new Blob([currentRawData], { type: 'application\/json' });\n        const url = URL.createObjectURL(blob);\n        const a = document.createElement('a');\n        a.href = url;\n        a.download = `sanepo-formatter-${new Date().getTime()}.json`;\n        document.body.appendChild(a);\n        a.click();\n        document.body.removeChild(a);\n        URL.revokeObjectURL(url);\n        showStatus('File downloaded!');\n    });\n\n    btnCopy.addEventListener('click', () => {\n        if (!currentRawData) return;\n        if (navigator.clipboard && window.isSecureContext) {\n            navigator.clipboard.writeText(currentRawData).then(() => showStatus('Copied to clipboard!'));\n        } else {\n            const textarea = document.createElement('textarea');\n            textarea.value = currentRawData;\n            textarea.style.position = 'fixed';\n            textarea.style.opacity = '0';\n            document.body.appendChild(textarea);\n            textarea.focus();\n            textarea.select();\n            try { document.execCommand('copy'); showStatus('Copied to clipboard!'); } \n            catch (err) { showStatus('Failed to copy', 'error'); }\n            document.body.removeChild(textarea);\n        }\n    });\n\n    \/\/ Control Listeners\n    inputArea.addEventListener('input', () => {\n        clearTimeout(inputArea.timer);\n        inputArea.timer = setTimeout(() => processJSON('beautify'), 300);\n    });\n    \n    filterInput.addEventListener('input', () => {\n        \/\/ Trigger formatting ulang saat filter diubah, gunakan objek yang sudah di-parse agar cepat\n        processJSON(isMinified ? 'minify' : 'beautify', lastParsedObj);\n    });\n\n    indentSelect.addEventListener('change', () => processJSON(isMinified ? 'minify' : 'beautify', lastParsedObj));\n    btnBeautify.addEventListener('click', () => processJSON('beautify', lastParsedObj));\n    btnMinify.addEventListener('click', () => processJSON('minify', lastParsedObj));\n    \n    btnSort.addEventListener('click', () => {\n        if(!lastParsedObj) return;\n        const sorted = sortObjectKeys(lastParsedObj);\n        processJSON(isMinified ? 'minify' : 'beautify', sorted);\n        showStatus('Keys sorted successfully!');\n    });\n    \n    btnFix.addEventListener('click', magicFixJSON);\n\n    \/\/ Initial Example State\n    inputArea.value = '{\\n  \"welcome\": \"To Sanepo Tools\",\\n  \"status\": \"Ready\",\\n  \"features\": [\"Beautify\", \"Minify\", \"Sort Keys\", \"Auto-Fix\", \"Drag & Drop\", \"JSON Path Filter\"],\\n  \"demo\": {\\n    \"nested\": true,\\n    \"try_filter\": \"Type demo.nested in the output filter\"\\n  }\\n}';\n    processJSON('beautify');\n\n})();\n<\/script>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sanepo Tools JSON Formatter &#038; Beautifier Clean, format, filter, and validate your JSON data securely in the browser. 2 Spaces4 SpacesTabs Sort Keys Auto-Fix Minify Beautify Copy Input 0 B Output<\/p>\n","protected":false},"featured_media":0,"template":"","meta":[],"class_list":["post-84","tool","type-tool","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/tools.sanepo.com\/es\/wp-json\/wp\/v2\/tool\/84","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tools.sanepo.com\/es\/wp-json\/wp\/v2\/tool"}],"about":[{"href":"https:\/\/tools.sanepo.com\/es\/wp-json\/wp\/v2\/types\/tool"}],"wp:attachment":[{"href":"https:\/\/tools.sanepo.com\/es\/wp-json\/wp\/v2\/media?parent=84"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}