Spaces:
Running
Running
🐳 10/02 - 13:09 - The output rendering on the bottom should let me edit--though I think it needws to be side by side to be useful; either side-by-side both views or select the jsonic-ified easier-to-
Browse files- index.html +92 -39
index.html
CHANGED
|
@@ -328,34 +328,45 @@
|
|
| 328 |
</div>
|
| 329 |
</div>
|
| 330 |
|
| 331 |
-
<div class="p-4 flex flex-col
|
| 332 |
-
<
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
<div
|
| 336 |
-
<
|
|
|
|
| 337 |
</div>
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
<div class="bg-gray-50 rounded-lg p-4">
|
| 341 |
-
<h2 class="text-lg font-semibold text-gray-700 mb-2">JSON Output</h2>
|
| 342 |
-
<textarea id="jsonOutput" class="w-full h-40 font-mono text-sm p-3 border rounded-lg bg-white" readonly></textarea>
|
| 343 |
-
<div class="mt-2 flex justify-between">
|
| 344 |
-
<button id="copyBtn" class="btn bg-gray-200 hover:bg-gray-300 px-4 py-2 rounded-lg">
|
| 345 |
-
<i class="fas fa-copy"></i> Copy JSON
|
| 346 |
-
</button>
|
| 347 |
-
<button id="downloadBtn" class="btn bg-primary hover:bg-secondary text-white px-4 py-2 rounded-lg">
|
| 348 |
-
<i class="fas fa-download"></i> Download
|
| 349 |
-
</button>
|
| 350 |
</div>
|
| 351 |
</div>
|
| 352 |
</div>
|
| 353 |
|
| 354 |
-
<
|
|
|
|
| 355 |
<div class="bg-gray-50 rounded-lg p-4 h-full">
|
| 356 |
-
<
|
| 357 |
-
|
| 358 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 359 |
</div>
|
| 360 |
</div>
|
| 361 |
</div>
|
|
@@ -431,6 +442,9 @@
|
|
| 431 |
const notification = document.getElementById('notification');
|
| 432 |
const copyBtn = document.getElementById('copyBtn');
|
| 433 |
const downloadBtn = document.getElementById('downloadBtn');
|
|
|
|
|
|
|
|
|
|
| 434 |
|
| 435 |
// Current state
|
| 436 |
let jsonData = {};
|
|
@@ -792,15 +806,60 @@
|
|
| 792 |
|
| 793 |
// Update JSON output
|
| 794 |
function updateOutput() {
|
|
|
|
| 795 |
try {
|
| 796 |
jsonOutput.value = JSON.stringify(jsonData, null, 2);
|
| 797 |
jsonOutput.classList.remove('error-highlight');
|
|
|
|
|
|
|
| 798 |
} catch (e) {
|
| 799 |
jsonOutput.value = 'Invalid JSON structure';
|
| 800 |
jsonOutput.classList.add('error-highlight');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 801 |
}
|
| 802 |
}
|
| 803 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 804 |
// Show notification
|
| 805 |
function showNotification(message) {
|
| 806 |
const notificationContent = notification.querySelector('p');
|
|
@@ -893,33 +952,27 @@
|
|
| 893 |
// Enable pasting in JSON output pane
|
| 894 |
jsonOutput.addEventListener('paste', (e) => {
|
| 895 |
e.preventDefault();
|
| 896 |
-
|
| 897 |
try {
|
| 898 |
const data = JSON.parse(text);
|
| 899 |
loadJSON(data);
|
| 900 |
-
showNotification('JSON pasted successfully');
|
| 901 |
} catch (parseError) {
|
| 902 |
-
//
|
| 903 |
const start = jsonOutput.selectionStart;
|
| 904 |
const end = jsonOutput.selectionEnd;
|
| 905 |
const currentValue = jsonOutput.value;
|
| 906 |
jsonOutput.value = currentValue.substring(0, start) + text + currentValue.substring(end);
|
| 907 |
-
showNotification('Pasted as plain text');
|
| 908 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 909 |
}).catch(err => {
|
| 910 |
-
// Fallback
|
| 911 |
const text = (e.originalEvent || e).clipboardData.getData('text/plain');
|
| 912 |
-
|
| 913 |
-
const data = JSON.parse(text);
|
| 914 |
-
loadJSON(data);
|
| 915 |
-
showNotification('JSON pasted successfully');
|
| 916 |
-
} catch (parseError) {
|
| 917 |
-
const start = jsonOutput.selectionStart;
|
| 918 |
-
const end = jsonOutput.selectionEnd;
|
| 919 |
-
const currentValue = jsonOutput.value;
|
| 920 |
-
jsonOutput.value = currentValue.substring(0, start) + text + currentValue.substring(end);
|
| 921 |
-
showNotification('Pasted as plain text');
|
| 922 |
-
}
|
| 923 |
});
|
| 924 |
});
|
| 925 |
|
|
@@ -1009,7 +1062,7 @@
|
|
| 1009 |
|
| 1010 |
// Instructions button
|
| 1011 |
document.getElementById('instructionsBtn').addEventListener('click', () => {
|
| 1012 |
-
showNotification('Double-click to edit
|
| 1013 |
});
|
| 1014 |
|
| 1015 |
// Sample JSON button
|
|
|
|
| 328 |
</div>
|
| 329 |
</div>
|
| 330 |
|
| 331 |
+
<div class="p-4 flex flex-col lg:flex-row gap-4">
|
| 332 |
+
<!-- Left Panel - Visual JSON Editor -->
|
| 333 |
+
<div class="w-full lg:w-1/2">
|
| 334 |
+
<div class="bg-slate-700 rounded-lg p-4 h-full">
|
| 335 |
+
<div class="flex justify-between items-center mb-3">
|
| 336 |
+
<h2 class="text-lg font-semibold text-slate-200">Visual Editor</h2>
|
| 337 |
+
<span class="text-xs text-slate-400 bg-slate-800 px-2 py-1 rounded">Double-click to edit</span>
|
| 338 |
</div>
|
| 339 |
+
<div id="jsonEditor" class="json-editor border rounded-lg p-4 font-mono min-h-[500px] max-h-[600px] overflow-auto">
|
| 340 |
+
<!-- JSON content will be rendered here -->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 341 |
</div>
|
| 342 |
</div>
|
| 343 |
</div>
|
| 344 |
|
| 345 |
+
<!-- Right Panel - JSON Code Editor -->
|
| 346 |
+
<div class="w-full lg:w-1/2">
|
| 347 |
<div class="bg-gray-50 rounded-lg p-4 h-full">
|
| 348 |
+
<div class="flex justify-between items-center mb-3">
|
| 349 |
+
<h2 class="text-lg font-semibold text-gray-700">Code Editor</h2>
|
| 350 |
+
<div class="flex gap-2">
|
| 351 |
+
<button id="applyCodeBtn" class="btn bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded-lg text-sm">
|
| 352 |
+
<i class="fas fa-check mr-1"></i> Apply
|
| 353 |
+
</button>
|
| 354 |
+
</div>
|
| 355 |
+
</div>
|
| 356 |
+
<textarea id="jsonOutput" class="w-full h-[500px] max-h-[600px] font-mono text-sm p-3 border rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none"></textarea>
|
| 357 |
+
<div class="mt-3 flex flex-wrap gap-2 justify-between">
|
| 358 |
+
<div class="flex gap-2">
|
| 359 |
+
<button id="copyBtn" class="btn bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-lg text-sm">
|
| 360 |
+
<i class="fas fa-copy mr-1"></i> Copy
|
| 361 |
+
</button>
|
| 362 |
+
<button id="downloadBtn" class="btn bg-primary hover:bg-secondary text-white px-3 py-2 rounded-lg text-sm">
|
| 363 |
+
<i class="fas fa-download mr-1"></i> Download
|
| 364 |
+
</button>
|
| 365 |
+
</div>
|
| 366 |
+
<div id="validationStatus" class="flex items-center gap-2 text-sm">
|
| 367 |
+
<i class="fas fa-check-circle text-green-500"></i>
|
| 368 |
+
<span class="text-green-600">Valid JSON</span>
|
| 369 |
+
</div>
|
| 370 |
</div>
|
| 371 |
</div>
|
| 372 |
</div>
|
|
|
|
| 442 |
const notification = document.getElementById('notification');
|
| 443 |
const copyBtn = document.getElementById('copyBtn');
|
| 444 |
const downloadBtn = document.getElementById('downloadBtn');
|
| 445 |
+
const applyCodeBtn = document.getElementById('applyCodeBtn');
|
| 446 |
+
const validationStatus = document.getElementById('validationStatus');
|
| 447 |
+
let isApplyingChanges = false;
|
| 448 |
|
| 449 |
// Current state
|
| 450 |
let jsonData = {};
|
|
|
|
| 806 |
|
| 807 |
// Update JSON output
|
| 808 |
function updateOutput() {
|
| 809 |
+
if (isApplyingChanges) return;
|
| 810 |
try {
|
| 811 |
jsonOutput.value = JSON.stringify(jsonData, null, 2);
|
| 812 |
jsonOutput.classList.remove('error-highlight');
|
| 813 |
+
jsonOutput.style.borderColor = '#d1d5db';
|
| 814 |
+
validationStatus.innerHTML = '<i class="fas fa-check-circle text-green-500"></i><span class="text-green-600">Valid JSON</span>';
|
| 815 |
} catch (e) {
|
| 816 |
jsonOutput.value = 'Invalid JSON structure';
|
| 817 |
jsonOutput.classList.add('error-highlight');
|
| 818 |
+
jsonOutput.style.borderColor = '#ef4444';
|
| 819 |
+
validationStatus.innerHTML = '<i class="fas fa-exclamation-circle text-red-500"></i><span class="text-red-600">Invalid JSON</span>';
|
| 820 |
+
}
|
| 821 |
+
}
|
| 822 |
+
|
| 823 |
+
// Apply changes from code editor to visual editor
|
| 824 |
+
function applyCodeChanges() {
|
| 825 |
+
try {
|
| 826 |
+
const newData = JSON.parse(jsonOutput.value);
|
| 827 |
+
isApplyingChanges = true;
|
| 828 |
+
loadJSON(newData);
|
| 829 |
+
isApplyingChanges = false;
|
| 830 |
+
showNotification('Changes applied successfully!');
|
| 831 |
+
return true;
|
| 832 |
+
} catch (e) {
|
| 833 |
+
showNotification('Invalid JSON: ' + e.message);
|
| 834 |
+
jsonOutput.style.borderColor = '#ef4444';
|
| 835 |
+
validationStatus.innerHTML = '<i class="fas fa-exclamation-circle text-red-500"></i><span class="text-red-600">Invalid JSON</span>';
|
| 836 |
+
return false;
|
| 837 |
}
|
| 838 |
}
|
| 839 |
|
| 840 |
+
// Real-time validation of code editor
|
| 841 |
+
jsonOutput.addEventListener('input', () => {
|
| 842 |
+
try {
|
| 843 |
+
JSON.parse(jsonOutput.value);
|
| 844 |
+
jsonOutput.style.borderColor = '#10b981';
|
| 845 |
+
validationStatus.innerHTML = '<i class="fas fa-check-circle text-green-500"></i><span class="text-green-600">Valid JSON</span>';
|
| 846 |
+
} catch (e) {
|
| 847 |
+
jsonOutput.style.borderColor = '#ef4444';
|
| 848 |
+
validationStatus.innerHTML = '<i class="fas fa-exclamation-circle text-red-500"></i><span class="text-red-600">Invalid JSON</span>';
|
| 849 |
+
}
|
| 850 |
+
});
|
| 851 |
+
|
| 852 |
+
// Apply button click
|
| 853 |
+
applyCodeBtn.addEventListener('click', applyCodeChanges);
|
| 854 |
+
|
| 855 |
+
// Auto-apply on Ctrl+Enter or Cmd+Enter
|
| 856 |
+
jsonOutput.addEventListener('keydown', (e) => {
|
| 857 |
+
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
| 858 |
+
e.preventDefault();
|
| 859 |
+
applyCodeChanges();
|
| 860 |
+
}
|
| 861 |
+
});
|
| 862 |
+
|
| 863 |
// Show notification
|
| 864 |
function showNotification(message) {
|
| 865 |
const notificationContent = notification.querySelector('p');
|
|
|
|
| 952 |
// Enable pasting in JSON output pane
|
| 953 |
jsonOutput.addEventListener('paste', (e) => {
|
| 954 |
e.preventDefault();
|
| 955 |
+
const pasteHandler = (text) => {
|
| 956 |
try {
|
| 957 |
const data = JSON.parse(text);
|
| 958 |
loadJSON(data);
|
| 959 |
+
showNotification('JSON pasted and loaded successfully');
|
| 960 |
} catch (parseError) {
|
| 961 |
+
// Paste as plain text at cursor position
|
| 962 |
const start = jsonOutput.selectionStart;
|
| 963 |
const end = jsonOutput.selectionEnd;
|
| 964 |
const currentValue = jsonOutput.value;
|
| 965 |
jsonOutput.value = currentValue.substring(0, start) + text + currentValue.substring(end);
|
| 966 |
+
showNotification('Pasted as plain text (use Apply button to load)');
|
| 967 |
}
|
| 968 |
+
};
|
| 969 |
+
|
| 970 |
+
navigator.clipboard.readText().then(text => {
|
| 971 |
+
pasteHandler(text);
|
| 972 |
}).catch(err => {
|
| 973 |
+
// Fallback for older browsers
|
| 974 |
const text = (e.originalEvent || e).clipboardData.getData('text/plain');
|
| 975 |
+
pasteHandler(text);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 976 |
});
|
| 977 |
});
|
| 978 |
|
|
|
|
| 1062 |
|
| 1063 |
// Instructions button
|
| 1064 |
document.getElementById('instructionsBtn').addEventListener('click', () => {
|
| 1065 |
+
showNotification('Visual: Double-click to edit | Code: Type and click Apply or Ctrl+Enter');
|
| 1066 |
});
|
| 1067 |
|
| 1068 |
// Sample JSON button
|