Spaces:
Running
Running

I tried to paste in clipboard contents of mcp_config.json but it again opened the deepsite editor pane - Follow Up Deployment
f80e511
verified
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Visual JSON Editor</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<script> | |
tailwind.config = { | |
theme: { | |
extend: { | |
colors: { | |
primary: '#3b82f6', | |
secondary: '#1e40af', | |
dark: '#0f172a', | |
light: '#f8fafc' | |
} | |
} | |
} | |
} | |
</script> | |
<style> | |
.json-editor { | |
min-height: 500px; | |
max-height: 70vh; | |
overflow-y: auto; | |
background-color: #1e293b; | |
background-image: radial-gradient(#334155 1px, transparent 1px); | |
background-size: 20px 20px; | |
padding: 20px; | |
border-radius: 8px; | |
} | |
.json-item { | |
transition: all 0.2s ease; | |
margin: 5px 0; | |
border-radius: 6px; | |
background-color: #334155; | |
box-shadow: 0 2px 4px rgba(0,0,0,0.2); | |
position: relative; | |
display: block; | |
} | |
.json-item-content { | |
padding: 8px 12px; | |
margin-left: 0; | |
display: inline-block; | |
min-width: 200px; | |
border-left: 3px solid transparent; | |
} | |
.json-item:hover .json-item-content { | |
background-color: #475569; | |
border-left: 3px solid #60a5fa; | |
} | |
.json-item.selected .json-item-content { | |
background-color: #475569; | |
border-left: 3px solid #60a5fa; | |
box-shadow: 0 0 0 2px rgba(96, 165, 250, 0.3); | |
} | |
.json-item.editing .json-item-content { | |
background-color: #64748b; | |
border-left: 3px solid #fbbf24; | |
box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.5); | |
} | |
.json-item:hover { | |
background-color: #475569; | |
border-left: 3px solid #60a5fa; | |
} | |
.json-item.selected { | |
background-color: #475569; | |
border-left: 3px solid #60a5fa; | |
box-shadow: 0 0 0 2px rgba(96, 165, 250, 0.3); | |
} | |
.json-item.editing { | |
background-color: #64748b; | |
border-left: 3px solid #fbbf24; | |
box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.5); | |
} | |
.json-item.dragging { | |
opacity: 0.5; | |
background-color: #475569; | |
} | |
.json-item.drag-over { | |
border-top: 2px dashed #60a5fa; | |
} | |
.json-key { | |
font-weight: 600; | |
color: #93c5fd; | |
margin-right: 8px; | |
} | |
.json-value { | |
color: #6ee7b7; | |
} | |
.json-bracket { | |
color: #94a3b8; | |
} | |
.btn { | |
transition: all 0.2s ease; | |
} | |
.btn:hover { | |
transform: translateY(-2px); | |
} | |
.indent-line { | |
position: absolute; | |
left: 0; | |
top: 0; | |
bottom: 0; | |
width: 1px; | |
background-color: #475569; | |
} | |
.cursor-pointer { | |
cursor: pointer; | |
} | |
.editable:focus { | |
outline: 2px solid #60a5fa; | |
border-radius: 4px; | |
} | |
.editable { | |
min-width: 20px; | |
display: inline-block; | |
background-color: rgba(96, 165, 250, 0.1); | |
padding: 2px 4px; | |
border-radius: 4px; | |
} | |
.toolbar-btn { | |
transition: all 0.2s; | |
} | |
.toolbar-btn:hover { | |
background-color: #475569; | |
} | |
.toolbar-btn.active { | |
background-color: #60a5fa; | |
color: white; | |
} | |
.notification { | |
transform: translateX(100%); | |
transition: transform 0.3s ease; | |
} | |
.notification.show { | |
transform: translateX(0); | |
} | |
.error-highlight { | |
background-color: #fee2e2; | |
border-left: 3px solid #ef4444; | |
} | |
.menu-bar { | |
background-color: #334155; | |
padding: 8px 16px; | |
border-radius: 8px; | |
margin-bottom: 16px; | |
box-shadow: 0 2px 4px rgba(0,0,0,0.2); | |
} | |
.menu-item { | |
color: #cbd5e1; | |
padding: 8px 12px; | |
border-radius: 4px; | |
cursor: pointer; | |
transition: all 0.2s; | |
margin-right: 4px; | |
} | |
.menu-item:hover { | |
background-color: #475569; | |
} | |
.menu-item.active { | |
background-color: #60a5fa; | |
color: white; | |
} | |
.dropdown { | |
position: relative; | |
display: inline-block; | |
} | |
.dropdown-content { | |
display: none; | |
position: absolute; | |
background-color: #334155; | |
min-width: 160px; | |
box-shadow: 0px 8px 16px rgba(0,0,0,0.2); | |
z-index: 1; | |
border-radius: 4px; | |
overflow: hidden; | |
} | |
.dropdown-content a { | |
color: #cbd5e1; | |
padding: 12px 16px; | |
text-decoration: none; | |
display: block; | |
} | |
.dropdown-content a:hover { | |
background-color: #475569; | |
} | |
.dropdown:hover .dropdown-content { | |
display: block; | |
} | |
.modal { | |
display: none; | |
position: fixed; | |
z-index: 1000; | |
left: 0; | |
top: 0; | |
width: 100%; | |
height: 100%; | |
background-color: rgba(0,0,0,0.5); | |
} | |
.modal-content { | |
background-color: #334155; | |
margin: 10% auto; | |
padding: 20px; | |
border-radius: 8px; | |
width: 80%; | |
max-width: 600px; | |
box-shadow: 0 4px 8px rgba(0,0,0,0.3); | |
color: #f1f5f9; | |
} | |
.close { | |
color: #94a3b8; | |
float: right; | |
font-size: 28px; | |
font-weight: bold; | |
cursor: pointer; | |
} | |
.close:hover { | |
color: #f1f5f9; | |
} | |
.boolean-select { | |
background-color: #475569; | |
color: #f1f5f9; | |
border: 1px solid #64748b; | |
border-radius: 4px; | |
padding: 2px 4px; | |
} | |
</style> | |
</head> | |
<body class="bg-slate-900 min-h-screen p-4 md:p-8"> | |
<div class="max-w-6xl mx-auto"> | |
<header class="mb-8 text-center"> | |
<h1 class="text-3xl md:text-4xl font-bold text-slate-100 mb-2">Visual JSON Editor</h1> | |
<p class="text-slate-300">Edit JSON with drag-and-drop and intuitive keyboard navigation</p> | |
</header> | |
<!-- Menu Bar --> | |
<div class="menu-bar flex flex-wrap"> | |
<div class="dropdown"> | |
<div class="menu-item">File</div> | |
<div class="dropdown-content"> | |
<a href="#" id="newBtn"><i class="fas fa-plus mr-2"></i>New</a> | |
<a href="#" id="openBtn"><i class="fas fa-folder-open mr-2"></i>Open</a> | |
<a href="#" id="saveBtn"><i class="fas fa-save mr-2"></i>Save</a> | |
</div> | |
</div> | |
<div class="dropdown"> | |
<div class="menu-item">Edit</div> | |
<div class="dropdown-content"> | |
<a href="#" id="undoBtn"><i class="fas fa-undo mr-2"></i>Undo</a> | |
<a href="#" id="redoBtn"><i class="fas fa-redo mr-2"></i>Redo</a> | |
<a href="#" id="copyBtnMenu"><i class="fas fa-copy mr-2"></i>Copy</a> | |
<a href="#" id="cutBtnMenu"><i class="fas fa-cut mr-2"></i>Cut</a> | |
<a href="#" id="pasteBtnMenu"><i class="fas fa-paste mr-2"></i>Paste</a> | |
<a href="#" id="preferencesBtn"><i class="fas fa-cog mr-2"></i>Preferences</a> | |
</div> | |
</div> | |
<div class="dropdown"> | |
<div class="menu-item">Tools</div> | |
<div class="dropdown-content"> | |
<a href="#" id="formatBtn"><i class="fas fa-indent mr-2"></i>Format</a> | |
<a href="#" id="validateBtn"><i class="fas fa-check-circle mr-2"></i>Validate</a> | |
</div> | |
</div> | |
<div class="dropdown"> | |
<div class="menu-item">View</div> | |
<div class="dropdown-content"> | |
<a href="#" id="viewModeBtn"><i class="fas fa-eye mr-2"></i>View Mode</a> | |
<a href="#" id="editModeBtn"><i class="fas fa-edit mr-2"></i>Edit Mode</a> | |
</div> | |
</div> | |
<div class="dropdown"> | |
<div class="menu-item">Help</div> | |
<div class="dropdown-content"> | |
<a href="#" id="instructionsBtn"><i class="fas fa-info-circle mr-2"></i>Instructions</a> | |
<a href="#" id="sampleBtn"><i class="fas fa-code mr-2"></i>Sample JSON</a> | |
</div> | |
</div> | |
</div> | |
<div class="bg-slate-800 rounded-xl shadow-lg overflow-hidden mb-8"> | |
<div class="p-4 bg-slate-700 border-b flex flex-wrap items-center justify-between gap-2"> | |
<div class="flex flex-wrap gap-2"> | |
<button id="newBtn2" class="btn bg-primary hover:bg-secondary text-white px-4 py-2 rounded-lg flex items-center gap-2"> | |
<i class="fas fa-plus"></i> New | |
</button> | |
<button id="openBtn2" class="btn bg-slate-600 hover:bg-slate-500 px-4 py-2 rounded-lg flex items-center gap-2"> | |
<i class="fas fa-folder-open"></i> Open | |
</button> | |
<button id="saveBtn2" class="btn bg-slate-600 hover:bg-slate-500 px-4 py-2 rounded-lg flex items-center gap-2"> | |
<i class="fas fa-save"></i> Save | |
</button> | |
</div> | |
<div class="flex flex-wrap gap-2"> | |
<button id="undoBtn2" class="btn bg-slate-600 hover:bg-slate-500 px-3 py-2 rounded-lg"> | |
<i class="fas fa-undo"></i> | |
</button> | |
<button id="redoBtn2" class="btn bg-slate-600 hover:bg-slate-500 px-3 py-2 rounded-lg"> | |
<i class="fas fa-redo"></i> | |
</button> | |
<button id="formatBtn2" class="btn bg-slate-600 hover:bg-slate-500 px-3 py-2 rounded-lg"> | |
<i class="fas fa-indent"></i> | |
</button> | |
<button id="validateBtn2" class="btn bg-slate-600 hover:bg-slate-500 px-3 py-2 rounded-lg"> | |
<i class="fas fa-check-circle"></i> | |
</button> | |
</div> | |
</div> | |
<div class="p-4 bg-gray-50 border-b flex flex-wrap gap-2"> | |
<div class="flex items-center gap-2 bg-white px-3 py-1 rounded-lg shadow-sm"> | |
<span class="text-gray-600">Mode:</span> | |
<div class="flex gap-1"> | |
<button id="viewModeBtn" class="toolbar-btn px-3 py-1 rounded-md">View</button> | |
<button id="editModeBtn" class="toolbar-btn px-3 py-1 rounded-md active">Edit</button> | |
</div> | |
</div> | |
<div class="flex items-center gap-2 bg-white px-3 py-1 rounded-lg shadow-sm"> | |
<span class="text-gray-600">Indent:</span> | |
<div class="flex gap-1"> | |
<button id="indentBtn" class="toolbar-btn px-3 py-1 rounded-md">2</button> | |
<button id="indentBtn4" class="toolbar-btn px-3 py-1 rounded-md">4</button> | |
</div> | |
</div> | |
</div> | |
<div class="p-4 flex flex-col md:flex-row gap-4"> | |
<div class="w-full"> | |
<div class="bg-slate-700 rounded-lg p-4 mb-4"> | |
<h2 class="text-lg font-semibold text-slate-200 mb-2">JSON Editor</h2> | |
<div id="jsonEditor" class="json-editor border rounded-lg p-4 font-mono min-h-[400px] relative"> | |
<!-- JSON content will be rendered here --> | |
</div> | |
</div> | |
<div class="bg-gray-50 rounded-lg p-4"> | |
<h2 class="text-lg font-semibold text-gray-700 mb-2">JSON Output</h2> | |
<textarea id="jsonOutput" class="w-full h-40 font-mono text-sm p-3 border rounded-lg bg-white" readonly></textarea> | |
<div class="mt-2 flex justify-between"> | |
<button id="copyBtn" class="btn bg-gray-200 hover:bg-gray-300 px-4 py-2 rounded-lg"> | |
<i class="fas fa-copy"></i> Copy JSON | |
</button> | |
<button id="downloadBtn" class="btn bg-primary hover:bg-secondary text-white px-4 py-2 rounded-lg"> | |
<i class="fas fa-download"></i> Download | |
</button> | |
</div> | |
</div> | |
</div> | |
<div class="w-full md:w-1/2"> | |
<div class="bg-gray-50 rounded-lg p-4 h-full"> | |
<h2 class="text-lg font-semibold text-gray-700 mb-2">Preview</h2> | |
<div class="bg-white border rounded-lg p-4 h-[400px] overflow-y-auto"> | |
<p class="text-gray-600">JSON structure will be displayed here in view mode.</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="bg-white rounded-xl shadow-lg p-6"> | |
<h2 class="text-xl font-bold text-gray-800 mb-4">How It Works</h2> | |
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> | |
<div class="bg-blue-50 p-4 rounded-lg border border-blue-200"> | |
<div class="text-blue-500 text-2xl mb-2"> | |
<i class="fas fa-magic"></i> | |
</div> | |
<h3 class="font-bold text-lg mb-2">Visual Editing</h3> | |
<p class="text-gray-700">Easily edit JSON without worrying about brackets or indentation. Each element is visually represented for intuitive editing.</p> | |
</div> | |
<div class="bg-green-50 p-4 rounded-lg border border-green-200"> | |
<div class="text-green-500 text-2xl mb-2"> | |
<i class="fas fa-sync-alt"></i> | |
</div> | |
<h3 class="font-bold text-lg mb-2">Real-time Sync</h3> | |
<p class="text-gray-700">Changes in the visual editor are immediately reflected in the JSON output, and vice versa. Validate your JSON at any time.</p> | |
</div> | |
<div class="bg-purple-50 p-4 rounded-lg border border-purple-200"> | |
<div class="text-purple-500 text-2xl mb-2"> | |
<i class="fas fa-robot"></i> | |
</div> | |
<h3 class="font-bold text-lg mb-2">Auto-Repair</h3> | |
<p class="text-gray-700">Paste malformed JSON and our editor will attempt to automatically repair common syntax errors and structural issues.</p> | |
</div> | |
</div> | |
</div> | |
<div id="notification" class="notification fixed bottom-4 right-4 bg-white shadow-lg rounded-lg p-4 border-l-4 border-green-500 max-w-md"> | |
<div class="flex items-start"> | |
<i class="fas fa-check-circle text-green-500 text-xl mt-0.5 mr-3"></i> | |
<div> | |
<h4 class="font-bold text-gray-800">Success!</h4> | |
<p class="text-gray-600 mt-1">Your JSON has been updated successfully.</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
document.addEventListener('DOMContentLoaded', function() { | |
// Sample JSON data | |
const sampleJSON = { | |
"name": "John Doe", | |
"age": 30, | |
"isStudent": false, | |
"address": { | |
"street": "123 Main St", | |
"city": "Anytown", | |
"zipcode": "12345" | |
}, | |
"hobbies": [ | |
"reading", | |
"swimming", | |
"coding" | |
], | |
"contact": { | |
"email": "[email protected]", | |
"phone": "555-1234" | |
} | |
}; | |
// DOM elements | |
const jsonEditor = document.getElementById('jsonEditor'); | |
const jsonOutput = document.getElementById('jsonOutput'); | |
const openBtn = document.getElementById('openBtn'); | |
const openBtn2 = document.getElementById('openBtn2'); | |
const notification = document.getElementById('notification'); | |
const copyBtn = document.getElementById('copyBtn'); | |
const downloadBtn = document.getElementById('downloadBtn'); | |
// Current state | |
let jsonData = {}; | |
let selectedElement = null; | |
let history = []; | |
let historyIndex = -1; | |
// Initialize with sample data | |
loadJSON(sampleJSON); | |
// Event listeners | |
// Create hidden file input for opening files | |
const fileInput = document.createElement('input'); | |
fileInput.type = 'file'; | |
fileInput.accept = 'application/json,.json'; | |
fileInput.style.display = 'none'; | |
document.body.appendChild(fileInput); | |
// Open file functionality | |
function openFile() { | |
fileInput.click(); | |
} | |
fileInput.addEventListener('change', (event) => { | |
const file = event.target.files[0]; | |
if (file) { | |
const reader = new FileReader(); | |
reader.onload = (e) => { | |
try { | |
const data = JSON.parse(e.target.result); | |
loadJSON(data); | |
showNotification('File loaded successfully!'); | |
} catch (error) { | |
showNotification('Error loading file: Invalid JSON'); | |
} | |
}; | |
reader.readAsText(file); | |
} | |
}); | |
openBtn.addEventListener('click', openFile); | |
openBtn2.addEventListener('click', openFile); | |
copyBtn.addEventListener('click', () => { | |
jsonOutput.select(); | |
document.execCommand('copy'); | |
showNotification('JSON copied to clipboard!'); | |
}); | |
downloadBtn.addEventListener('click', () => { | |
const blob = new Blob([jsonOutput.value], { type: 'application/json' }); | |
const url = URL.createObjectURL(blob); | |
const a = document.createElement('a'); | |
a.href = url; | |
a.download = 'data.json'; | |
document.body.appendChild(a); | |
a.click(); | |
document.body.removeChild(a); | |
URL.revokeObjectURL(url); | |
showNotification('JSON file downloaded!'); | |
}); | |
// Load JSON data into the editor | |
function loadJSON(data) { | |
jsonData = JSON.parse(JSON.stringify(data)); // Deep copy | |
renderEditor(); | |
updateOutput(); | |
saveToHistory(); | |
} | |
// Render the JSON editor | |
function renderEditor() { | |
jsonEditor.innerHTML = ''; | |
renderElement(jsonEditor, jsonData, 0, 'root'); | |
} | |
// Render a single JSON element | |
function renderElement(container, data, depth, key = null, parentKey = null) { | |
const wrapper = document.createElement('div'); | |
wrapper.className = 'json-item relative'; | |
wrapper.dataset.key = key; | |
wrapper.dataset.parent = parentKey; | |
wrapper.dataset.depth = depth; | |
// Add indent lines | |
if (depth > 0) { | |
const indentLine = document.createElement('div'); | |
indentLine.className = 'indent-line'; | |
indentLine.style.left = `${depth * 20}px`; | |
wrapper.appendChild(indentLine); | |
} | |
// Create element content | |
const content = document.createElement('div'); | |
content.className = 'json-item-content flex items-start py-1'; | |
content.style.marginLeft = `${depth * 20 + 8}px`; | |
// Key | |
if (key !== null && key !== 'root') { | |
const keyElement = document.createElement('span'); | |
keyElement.className = 'json-key mr-2'; | |
keyElement.textContent = `"${key}": `; | |
content.appendChild(keyElement); | |
} | |
// Value or children | |
if (typeof data === 'object' && data !== null) { | |
if (Array.isArray(data)) { | |
// Array | |
const bracket = document.createElement('span'); | |
bracket.className = 'json-bracket'; | |
bracket.textContent = '['; | |
content.appendChild(bracket); | |
wrapper.appendChild(content); | |
container.appendChild(wrapper); | |
// Render array items | |
data.forEach((item, index) => { | |
renderElement(container, item, depth + 1, index, key); | |
}); | |
// Closing bracket | |
const closingWrapper = document.createElement('div'); | |
closingWrapper.className = 'json-item relative'; | |
closingWrapper.dataset.key = 'closing'; | |
closingWrapper.dataset.parent = key; | |
closingWrapper.dataset.depth = depth; | |
const closingContent = document.createElement('div'); | |
closingContent.className = 'json-item-content flex items-start py-1'; | |
closingContent.style.marginLeft = `${depth * 20 + 8}px`; | |
if (depth > 0) { | |
const indentLine = document.createElement('div'); | |
indentLine.className = 'indent-line'; | |
indentLine.style.left = `${depth * 20}px`; | |
closingWrapper.appendChild(indentLine); | |
} | |
const closingContent = document.createElement('div'); | |
closingContent.className = 'flex items-start py-1'; | |
const closingBracket = document.createElement('span'); | |
closingBracket.className = 'json-bracket'; | |
closingBracket.textContent = ']'; | |
closingContent.appendChild(closingBracket); | |
closingWrapper.appendChild(closingContent); | |
container.appendChild(closingWrapper); | |
} else { | |
// Object | |
const bracket = document.createElement('span'); | |
bracket.className = 'json-bracket'; | |
bracket.textContent = '{'; | |
content.appendChild(bracket); | |
wrapper.appendChild(content); | |
container.appendChild(wrapper); | |
// Render object properties | |
Object.keys(data).forEach(propKey => { | |
renderElement(container, data[propKey], depth + 1, propKey, key); | |
}); | |
// Closing bracket | |
const closingWrapper = document.createElement('div'); | |
closingWrapper.className = 'json-item relative'; | |
closingWrapper.dataset.key = 'closing'; | |
closingWrapper.dataset.parent = key; | |
closingWrapper.dataset.depth = depth; | |
const closingContent = document.createElement('div'); | |
closingContent.className = 'json-item-content flex items-start py-1'; | |
closingContent.style.marginLeft = `${depth * 20 + 8}px`; | |
if (depth > 0) { | |
const indentLine = document.createElement('div'); | |
indentLine.className = 'indent-line'; | |
indentLine.style.left = `${depth * 20}px`; | |
closingWrapper.appendChild(indentLine); | |
} | |
const closingContent = document.createElement('div'); | |
closingContent.className = 'flex items-start py-1'; | |
const closingBracket = document.createElement('span'); | |
closingBracket.className = 'json-bracket'; | |
closingBracket.textContent = '}'; | |
closingContent.appendChild(closingBracket); | |
closingWrapper.appendChild(closingContent); | |
container.appendChild(closingWrapper); | |
} | |
} else { | |
// Primitive value | |
const valueElement = document.createElement('span'); | |
valueElement.className = 'json-value'; | |
if (typeof data === 'string') { | |
valueElement.textContent = `"${data}"`; | |
} else if (typeof data === 'boolean') { | |
valueElement.textContent = data.toString(); | |
} else { | |
valueElement.textContent = data; | |
} | |
content.appendChild(valueElement); | |
wrapper.appendChild(content); | |
container.appendChild(wrapper); | |
} | |
// Add event listeners for editing | |
if (wrapper.dataset.key !== 'closing') { | |
wrapper.addEventListener('dblclick', () => editElement(wrapper)); | |
wrapper.addEventListener('click', () => selectElement(wrapper)); | |
} | |
} | |
// Edit an element | |
function editElement(element) { | |
if (element.dataset.key === 'closing') return; | |
const keyElement = element.querySelector('.json-key'); | |
const valueElement = element.querySelector('.json-value'); | |
if (keyElement) { | |
const keyText = keyElement.textContent.replace(/"/g, '').replace(':', '').trim(); | |
const input = document.createElement('input'); | |
input.type = 'text'; | |
input.className = 'editable bg-yellow-100 px-1'; | |
input.value = keyText; | |
keyElement.replaceWith(input); | |
input.focus(); | |
input.addEventListener('blur', () => { | |
const newKey = input.value; | |
updateKey(element, newKey); | |
input.replaceWith(keyElement); | |
keyElement.textContent = `"${newKey}": `; | |
}); | |
input.addEventListener('keydown', (e) => { | |
if (e.key === 'Enter') { | |
input.blur(); | |
} | |
}); | |
} | |
if (valueElement) { | |
const valueText = valueElement.textContent.replace(/"/g, ''); | |
const input = document.createElement('input'); | |
input.type = 'text'; | |
input.className = 'editable bg-yellow-100 px-1'; | |
input.value = valueText; | |
valueElement.replaceWith(input); | |
input.focus(); | |
input.addEventListener('blur', () => { | |
const newValue = parseValue(input.value); | |
updateValue(element, newValue); | |
input.replaceWith(valueElement); | |
valueElement.textContent = formatValue(newValue); | |
}); | |
input.addEventListener('keydown', (e) => { | |
if (e.key === 'Enter') { | |
input.blur(); | |
} | |
}); | |
} | |
} | |
// Parse a value from string to appropriate type | |
function parseValue(value) { | |
if (value === 'true') return true; | |
if (value === 'false') return false; | |
if (value === 'null') return null; | |
if (!isNaN(value) && value.trim() !== '') return Number(value); | |
return value; | |
} | |
// Format a value for display | |
function formatValue(value) { | |
if (typeof value === 'string') return `"${value}"`; | |
if (typeof value === 'boolean') return value.toString(); | |
if (value === null) return 'null'; | |
return value; | |
} | |
// Update a key | |
function updateKey(element, newKey) { | |
const parentKey = element.dataset.parent; | |
const oldKey = element.dataset.key; | |
if (parentKey === 'root') { | |
const newData = {}; | |
Object.keys(jsonData).forEach(key => { | |
if (key === oldKey) { | |
newData[newKey] = jsonData[key]; | |
} else if (key !== newKey) { | |
newData[key] = jsonData[key]; | |
} | |
}); | |
jsonData = newData; | |
} else { | |
// Find parent in nested structure | |
const parent = findElementByKey(jsonData, parentKey); | |
if (parent && typeof parent === 'object') { | |
const newData = {}; | |
Object.keys(parent).forEach(key => { | |
if (key === oldKey) { | |
newData[newKey] = parent[key]; | |
} else if (key !== newKey) { | |
newData[key] = parent[key]; | |
} | |
}); | |
Object.keys(parent).forEach(key => delete parent[key]); | |
Object.assign(parent, newData); | |
} | |
} | |
element.dataset.key = newKey; | |
updateOutput(); | |
saveToHistory(); | |
} | |
// Update a value | |
function updateValue(element, newValue) { | |
const key = element.dataset.key; | |
const parentKey = element.dataset.parent; | |
if (parentKey === 'root') { | |
jsonData[key] = newValue; | |
} else { | |
const parent = findElementByKey(jsonData, parentKey); | |
if (parent && typeof parent === 'object') { | |
parent[key] = newValue; | |
} | |
} | |
updateOutput(); | |
saveToHistory(); | |
} | |
// Find an element by key in nested structure | |
function findElementByKey(obj, key) { | |
if (obj[key] !== undefined) return obj; | |
for (let prop in obj) { | |
if (typeof obj[prop] === 'object' && obj[prop] !== null) { | |
const result = findElementByKey(obj[prop], key); | |
if (result) return result; | |
} | |
} | |
return null; | |
} | |
// Select an element | |
function selectElement(element) { | |
if (selectedElement) { | |
selectedElement.classList.remove('selected'); | |
} | |
element.classList.add('selected'); | |
selectedElement = element; | |
} | |
// Update JSON output | |
function updateOutput() { | |
try { | |
jsonOutput.value = JSON.stringify(jsonData, null, 2); | |
jsonOutput.classList.remove('error-highlight'); | |
} catch (e) { | |
jsonOutput.value = 'Invalid JSON structure'; | |
jsonOutput.classList.add('error-highlight'); | |
} | |
} | |
// Show notification | |
function showNotification(message) { | |
const notificationContent = notification.querySelector('p'); | |
notificationContent.textContent = message; | |
notification.classList.add('show'); | |
setTimeout(() => { | |
notification.classList.remove('show'); | |
}, 3000); | |
} | |
// Save to history for undo/redo | |
function saveToHistory() { | |
// Remove future history if we're not at the end | |
if (historyIndex < history.length - 1) { | |
history = history.slice(0, historyIndex + 1); | |
} | |
history.push(JSON.parse(JSON.stringify(jsonData))); | |
historyIndex = history.length - 1; | |
} | |
// Undo action | |
function undo() { | |
if (historyIndex > 0) { | |
historyIndex--; | |
jsonData = JSON.parse(JSON.stringify(history[historyIndex])); | |
renderEditor(); | |
updateOutput(); | |
showNotification('Undo successful'); | |
} | |
} | |
// Redo action | |
function redo() { | |
if (historyIndex < history.length - 1) { | |
historyIndex++; | |
jsonData = JSON.parse(JSON.stringify(history[historyIndex])); | |
renderEditor(); | |
updateOutput(); | |
showNotification('Redo successful'); | |
} | |
} | |
// Event listeners for undo/redo | |
document.getElementById('undoBtn').addEventListener('click', undo); | |
document.getElementById('redoBtn').addEventListener('click', redo); | |
// Event listeners for copy, cut, paste | |
document.getElementById('copyBtnMenu').addEventListener('click', () => { | |
if (selectedElement) { | |
const range = document.createRange(); | |
range.selectNode(selectedElement); | |
window.getSelection().removeAllRanges(); | |
window.getSelection().addRange(range); | |
document.execCommand('copy'); | |
window.getSelection().removeAllRanges(); | |
showNotification('Copied to clipboard'); | |
} | |
}); | |
document.getElementById('cutBtnMenu').addEventListener('click', () => { | |
if (selectedElement) { | |
const range = document.createRange(); | |
range.selectNode(selectedElement); | |
window.getSelection().removeAllRanges(); | |
window.getSelection().addRange(range); | |
document.execCommand('cut'); | |
window.getSelection().removeAllRanges(); | |
showNotification('Cut to clipboard'); | |
} | |
}); | |
document.getElementById('pasteBtnMenu').addEventListener('click', () => { | |
navigator.clipboard.readText().then(text => { | |
// Try to parse as JSON and load if valid | |
try { | |
const data = JSON.parse(text); | |
loadJSON(data); | |
showNotification('JSON pasted successfully'); | |
} catch (e) { | |
// If not valid JSON, just show notification | |
showNotification('Clipboard content is not valid JSON'); | |
} | |
}).catch(err => { | |
showNotification('Failed to read clipboard contents'); | |
}); | |
}); | |
// Enable pasting in JSON output pane | |
jsonOutput.addEventListener('paste', (e) => { | |
e.preventDefault(); | |
navigator.clipboard.readText().then(text => { | |
try { | |
const data = JSON.parse(text); | |
loadJSON(data); | |
showNotification('JSON pasted successfully'); | |
} catch (parseError) { | |
// If not valid JSON, paste as plain text | |
const start = jsonOutput.selectionStart; | |
const end = jsonOutput.selectionEnd; | |
const currentValue = jsonOutput.value; | |
jsonOutput.value = currentValue.substring(0, start) + text + currentValue.substring(end); | |
showNotification('Pasted as plain text'); | |
} | |
}).catch(err => { | |
// Fallback to clipboardData for older browsers | |
const text = (e.originalEvent || e).clipboardData.getData('text/plain'); | |
try { | |
const data = JSON.parse(text); | |
loadJSON(data); | |
showNotification('JSON pasted successfully'); | |
} catch (parseError) { | |
const start = jsonOutput.selectionStart; | |
const end = jsonOutput.selectionEnd; | |
const currentValue = jsonOutput.value; | |
jsonOutput.value = currentValue.substring(0, start) + text + currentValue.substring(end); | |
showNotification('Pasted as plain text'); | |
} | |
}); | |
}); | |
// Enable pasting in JSON editor pane | |
jsonEditor.addEventListener('paste', (e) => { | |
e.preventDefault(); | |
navigator.clipboard.readText().then(text => { | |
try { | |
const data = JSON.parse(text); | |
loadJSON(data); | |
showNotification('JSON pasted successfully'); | |
} catch (parseError) { | |
showNotification('Clipboard content is not valid JSON'); | |
} | |
}).catch(err => { | |
// Fallback to clipboardData for older browsers | |
const text = (e.originalEvent || e).clipboardData.getData('text/plain'); | |
try { | |
const data = JSON.parse(text); | |
loadJSON(data); | |
showNotification('JSON pasted successfully'); | |
} catch (parseError) { | |
showNotification('Clipboard content is not valid JSON'); | |
} | |
}); | |
}); | |
// Format JSON | |
document.getElementById('formatBtn').addEventListener('click', () => { | |
updateOutput(); | |
showNotification('JSON formatted'); | |
}); | |
// Validate JSON | |
document.getElementById('validateBtn').addEventListener('click', () => { | |
try { | |
JSON.parse(jsonOutput.value); | |
showNotification('JSON is valid!'); | |
} catch (e) { | |
showNotification('Invalid JSON: ' + e.message); | |
} | |
}); | |
}); | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=RobinsAIWorld/jsonic-v2" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |