RobinsAIWorld commited on
Commit
e912915
·
verified ·
1 Parent(s): e12fc67

Make boolean choices selectable combo-boxes; drop-down can be changed with up or down arrows, but can also be changed to text elements by typing into the combo box. Make the rendering more like a visual flow rendering and less techie looking, showing indent levels more clearly less reliant on the brackets and more like graphical elements. Make the background a dark backgrounf with a faint grid or dot-grid . Make the instructions available from the File Edit View Tools Help menu and instead of starting with demo content, start blank, but add the demo content to the Help menu and the instructions . Use a split-screen format by default, where pasting into either side pastes json to one side and 'visual graph/flow' on the other - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +729 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Jsonic V2
3
- emoji: 👀
4
- colorFrom: indigo
5
- colorTo: blue
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: jsonic-v2
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: green
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,729 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Visual JSON Editor</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <script>
10
+ tailwind.config = {
11
+ theme: {
12
+ extend: {
13
+ colors: {
14
+ primary: '#3b82f6',
15
+ secondary: '#1e40af',
16
+ dark: '#0f172a',
17
+ light: '#f8fafc'
18
+ }
19
+ }
20
+ }
21
+ }
22
+ </script>
23
+ <style>
24
+ .json-editor {
25
+ min-height: 500px;
26
+ max-height: 70vh;
27
+ overflow-y: auto;
28
+ }
29
+ .json-item {
30
+ transition: all 0.2s ease;
31
+ border-left: 3px solid transparent;
32
+ }
33
+ .json-item:hover {
34
+ background-color: #f1f5f9;
35
+ border-left: 3px solid #3b82f6;
36
+ }
37
+ .json-item.selected {
38
+ background-color: #dbeafe;
39
+ border-left: 3px solid #3b82f6;
40
+ }
41
+ .json-item.dragging {
42
+ opacity: 0.5;
43
+ background-color: #dbeafe;
44
+ }
45
+ .json-item.drag-over {
46
+ border-top: 2px dashed #3b82f6;
47
+ }
48
+ .json-key {
49
+ font-weight: 600;
50
+ color: #4338ca;
51
+ }
52
+ .json-value {
53
+ color: #0f766e;
54
+ }
55
+ .json-bracket {
56
+ color: #64748b;
57
+ }
58
+ .btn {
59
+ transition: all 0.2s ease;
60
+ }
61
+ .btn:hover {
62
+ transform: translateY(-2px);
63
+ }
64
+ .indent-line {
65
+ position: absolute;
66
+ left: 0;
67
+ top: 0;
68
+ bottom: 0;
69
+ width: 1px;
70
+ background-color: #cbd5e1;
71
+ }
72
+ .cursor-pointer {
73
+ cursor: pointer;
74
+ }
75
+ .editable:focus {
76
+ outline: 2px solid #3b82f6;
77
+ border-radius: 4px;
78
+ }
79
+ .editable {
80
+ min-width: 20px;
81
+ display: inline-block;
82
+ }
83
+ .toolbar-btn {
84
+ transition: all 0.2s;
85
+ }
86
+ .toolbar-btn:hover {
87
+ background-color: #e2e8f0;
88
+ }
89
+ .toolbar-btn.active {
90
+ background-color: #3b82f6;
91
+ color: white;
92
+ }
93
+ .notification {
94
+ transform: translateX(100%);
95
+ transition: transform 0.3s ease;
96
+ }
97
+ .notification.show {
98
+ transform: translateX(0);
99
+ }
100
+ .error-highlight {
101
+ background-color: #fee2e2;
102
+ border-left: 3px solid #ef4444;
103
+ }
104
+ </style>
105
+ </head>
106
+ <body class="bg-gray-100 min-h-screen p-4 md:p-8">
107
+ <div class="max-w-6xl mx-auto">
108
+ <header class="mb-8 text-center">
109
+ <h1 class="text-3xl md:text-4xl font-bold text-gray-800 mb-2">Visual JSON Editor</h1>
110
+ <p class="text-gray-600">Edit JSON with drag-and-drop and intuitive keyboard navigation</p>
111
+ </header>
112
+
113
+ <div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8">
114
+ <div class="p-4 bg-gray-50 border-b flex flex-wrap items-center justify-between gap-2">
115
+ <div class="flex flex-wrap gap-2">
116
+ <button id="newBtn" class="btn bg-primary hover:bg-secondary text-white px-4 py-2 rounded-lg flex items-center gap-2">
117
+ <i class="fas fa-plus"></i> New
118
+ </button>
119
+ <button id="loadBtn" class="btn bg-gray-200 hover:bg-gray-300 px-4 py-2 rounded-lg flex items-center gap-2">
120
+ <i class="fas fa-folder-open"></i> Load
121
+ </button>
122
+ <button id="saveBtn" class="btn bg-gray-200 hover:bg-gray-300 px-4 py-2 rounded-lg flex items-center gap-2">
123
+ <i class="fas fa-save"></i> Save
124
+ </button>
125
+ </div>
126
+
127
+ <div class="flex flex-wrap gap-2">
128
+ <button id="undoBtn" class="btn bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-lg">
129
+ <i class="fas fa-undo"></i>
130
+ </button>
131
+ <button id="redoBtn" class="btn bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-lg">
132
+ <i class="fas fa-redo"></i>
133
+ </button>
134
+ <button id="formatBtn" class="btn bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-lg">
135
+ <i class="fas fa-indent"></i>
136
+ </button>
137
+ <button id="validateBtn" class="btn bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-lg">
138
+ <i class="fas fa-check-circle"></i>
139
+ </button>
140
+ </div>
141
+ </div>
142
+
143
+ <div class="p-4 bg-gray-50 border-b flex flex-wrap gap-2">
144
+ <div class="flex items-center gap-2 bg-white px-3 py-1 rounded-lg shadow-sm">
145
+ <span class="text-gray-600">Mode:</span>
146
+ <div class="flex gap-1">
147
+ <button id="viewModeBtn" class="toolbar-btn px-3 py-1 rounded-md">View</button>
148
+ <button id="editModeBtn" class="toolbar-btn px-3 py-1 rounded-md active">Edit</button>
149
+ </div>
150
+ </div>
151
+
152
+ <div class="flex items-center gap-2 bg-white px-3 py-1 rounded-lg shadow-sm">
153
+ <span class="text-gray-600">Indent:</span>
154
+ <div class="flex gap-1">
155
+ <button id="indentBtn" class="toolbar-btn px-3 py-1 rounded-md">2</button>
156
+ <button id="indentBtn4" class="toolbar-btn px-3 py-1 rounded-md">4</button>
157
+ </div>
158
+ </div>
159
+ </div>
160
+
161
+ <div class="p-4 flex flex-col md:flex-row gap-4">
162
+ <div class="w-full md:w-1/2">
163
+ <div class="bg-gray-50 rounded-lg p-4 mb-4">
164
+ <h2 class="text-lg font-semibold text-gray-700 mb-2">JSON Editor</h2>
165
+ <div id="jsonEditor" class="json-editor bg-white border rounded-lg p-4 font-mono min-h-[400px] relative">
166
+ <!-- JSON content will be rendered here -->
167
+ </div>
168
+ </div>
169
+
170
+ <div class="bg-gray-50 rounded-lg p-4">
171
+ <h2 class="text-lg font-semibold text-gray-700 mb-2">JSON Output</h2>
172
+ <textarea id="jsonOutput" class="w-full h-40 font-mono text-sm p-3 border rounded-lg bg-white" readonly></textarea>
173
+ <div class="mt-2 flex justify-between">
174
+ <button id="copyBtn" class="btn bg-gray-200 hover:bg-gray-300 px-4 py-2 rounded-lg">
175
+ <i class="fas fa-copy"></i> Copy JSON
176
+ </button>
177
+ <button id="downloadBtn" class="btn bg-primary hover:bg-secondary text-white px-4 py-2 rounded-lg">
178
+ <i class="fas fa-download"></i> Download
179
+ </button>
180
+ </div>
181
+ </div>
182
+ </div>
183
+
184
+ <div class="w-full md:w-1/2">
185
+ <div class="bg-gray-50 rounded-lg p-4 h-full">
186
+ <h2 class="text-lg font-semibold text-gray-700 mb-2">Instructions</h2>
187
+ <div class="bg-white border rounded-lg p-4 h-[400px] overflow-y-auto">
188
+ <ul class="space-y-3 text-gray-700">
189
+ <li class="flex items-start">
190
+ <i class="fas fa-mouse-pointer text-primary mt-1 mr-2"></i>
191
+ <div>
192
+ <span class="font-medium">Double-click</span> to edit any element
193
+ </div>
194
+ </li>
195
+ <li class="flex items-start">
196
+ <i class="fas fa-arrows-alt text-primary mt-1 mr-2"></i>
197
+ <div>
198
+ <span class="font-medium">Drag and drop</span> elements to reorganize
199
+ </div>
200
+ </li>
201
+ <li class="flex items-start">
202
+ <i class="fas fa-keyboard text-primary mt-1 mr-2"></i>
203
+ <div>
204
+ <span class="font-medium">Keyboard navigation:</span>
205
+ <ul class="ml-5 mt-1 space-y-1">
206
+ <li>• <span class="font-mono">Tab</span> / <span class="font-mono">Shift+Tab</span> to change indent level</li>
207
+ <li>• <span class="font-mono">Enter</span> to create new element at same level</li>
208
+ <li>• <span class="font-mono">Shift+Enter</span> to create child element</li>
209
+ <li>• <span class="font-mono">Arrow keys</span> to navigate between elements</li>
210
+ <li>• <span class="font-mono">Delete</span> to remove selected element</li>
211
+ </ul>
212
+ </div>
213
+ </li>
214
+ <li class="flex items-start">
215
+ <i class="fas fa-paste text-primary mt-1 mr-2"></i>
216
+ <div>
217
+ <span class="font-medium">Paste JSON</span> to automatically parse and render
218
+ </div>
219
+ </li>
220
+ <li class="flex items-start">
221
+ <i class="fas fa-wrench text-primary mt-1 mr-2"></i>
222
+ <div>
223
+ <span class="font-medium">Auto-repair</span> fixes common JSON syntax errors
224
+ </div>
225
+ </li>
226
+ </ul>
227
+
228
+ <div class="mt-6 p-4 bg-blue-50 rounded-lg border border-blue-200">
229
+ <h3 class="font-semibold text-blue-800 mb-2">Try this sample JSON:</h3>
230
+ <pre class="text-sm bg-white p-3 rounded overflow-x-auto">{
231
+ "name": "John Doe",
232
+ "age": 30,
233
+ "address": {
234
+ "street": "123 Main St",
235
+ "city": "Anytown"
236
+ },
237
+ "hobbies": [
238
+ "reading",
239
+ "swimming"
240
+ ]
241
+ }</pre>
242
+ <button id="loadSampleBtn" class="mt-3 btn bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg">
243
+ Load Sample
244
+ </button>
245
+ </div>
246
+ </div>
247
+ </div>
248
+ </div>
249
+ </div>
250
+ </div>
251
+
252
+ <div class="bg-white rounded-xl shadow-lg p-6">
253
+ <h2 class="text-xl font-bold text-gray-800 mb-4">How It Works</h2>
254
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
255
+ <div class="bg-blue-50 p-4 rounded-lg border border-blue-200">
256
+ <div class="text-blue-500 text-2xl mb-2">
257
+ <i class="fas fa-magic"></i>
258
+ </div>
259
+ <h3 class="font-bold text-lg mb-2">Visual Editing</h3>
260
+ <p class="text-gray-700">Easily edit JSON without worrying about brackets or indentation. Each element is visually represented for intuitive editing.</p>
261
+ </div>
262
+ <div class="bg-green-50 p-4 rounded-lg border border-green-200">
263
+ <div class="text-green-500 text-2xl mb-2">
264
+ <i class="fas fa-sync-alt"></i>
265
+ </div>
266
+ <h3 class="font-bold text-lg mb-2">Real-time Sync</h3>
267
+ <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>
268
+ </div>
269
+ <div class="bg-purple-50 p-4 rounded-lg border border-purple-200">
270
+ <div class="text-purple-500 text-2xl mb-2">
271
+ <i class="fas fa-robot"></i>
272
+ </div>
273
+ <h3 class="font-bold text-lg mb-2">Auto-Repair</h3>
274
+ <p class="text-gray-700">Paste malformed JSON and our editor will attempt to automatically repair common syntax errors and structural issues.</p>
275
+ </div>
276
+ </div>
277
+ </div>
278
+
279
+ <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">
280
+ <div class="flex items-start">
281
+ <i class="fas fa-check-circle text-green-500 text-xl mt-0.5 mr-3"></i>
282
+ <div>
283
+ <h4 class="font-bold text-gray-800">Success!</h4>
284
+ <p class="text-gray-600 mt-1">Your JSON has been updated successfully.</p>
285
+ </div>
286
+ </div>
287
+ </div>
288
+ </div>
289
+
290
+ <script>
291
+ document.addEventListener('DOMContentLoaded', function() {
292
+ // Sample JSON data
293
+ const sampleJSON = {
294
+ "name": "John Doe",
295
+ "age": 30,
296
+ "isStudent": false,
297
+ "address": {
298
+ "street": "123 Main St",
299
+ "city": "Anytown",
300
+ "zipcode": "12345"
301
+ },
302
+ "hobbies": [
303
+ "reading",
304
+ "swimming",
305
+ "coding"
306
+ ],
307
+ "contact": {
308
+ "email": "[email protected]",
309
+ "phone": "555-1234"
310
+ }
311
+ };
312
+
313
+ // DOM elements
314
+ const jsonEditor = document.getElementById('jsonEditor');
315
+ const jsonOutput = document.getElementById('jsonOutput');
316
+ const loadSampleBtn = document.getElementById('loadSampleBtn');
317
+ const notification = document.getElementById('notification');
318
+ const copyBtn = document.getElementById('copyBtn');
319
+ const downloadBtn = document.getElementById('downloadBtn');
320
+
321
+ // Current state
322
+ let jsonData = {};
323
+ let selectedElement = null;
324
+ let history = [];
325
+ let historyIndex = -1;
326
+
327
+ // Initialize with sample data
328
+ loadJSON(sampleJSON);
329
+
330
+ // Event listeners
331
+ loadSampleBtn.addEventListener('click', () => {
332
+ loadJSON(sampleJSON);
333
+ showNotification('Sample JSON loaded successfully!');
334
+ });
335
+
336
+ copyBtn.addEventListener('click', () => {
337
+ jsonOutput.select();
338
+ document.execCommand('copy');
339
+ showNotification('JSON copied to clipboard!');
340
+ });
341
+
342
+ downloadBtn.addEventListener('click', () => {
343
+ const blob = new Blob([jsonOutput.value], { type: 'application/json' });
344
+ const url = URL.createObjectURL(blob);
345
+ const a = document.createElement('a');
346
+ a.href = url;
347
+ a.download = 'data.json';
348
+ document.body.appendChild(a);
349
+ a.click();
350
+ document.body.removeChild(a);
351
+ URL.revokeObjectURL(url);
352
+ showNotification('JSON file downloaded!');
353
+ });
354
+
355
+ // Load JSON data into the editor
356
+ function loadJSON(data) {
357
+ jsonData = JSON.parse(JSON.stringify(data)); // Deep copy
358
+ renderEditor();
359
+ updateOutput();
360
+ saveToHistory();
361
+ }
362
+
363
+ // Render the JSON editor
364
+ function renderEditor() {
365
+ jsonEditor.innerHTML = '';
366
+ renderElement(jsonEditor, jsonData, 0, 'root');
367
+ }
368
+
369
+ // Render a single JSON element
370
+ function renderElement(container, data, depth, key = null, parentKey = null) {
371
+ const wrapper = document.createElement('div');
372
+ wrapper.className = 'json-item relative pl-6';
373
+ wrapper.style.paddingLeft = `${depth * 20 + 8}px`;
374
+ wrapper.dataset.key = key;
375
+ wrapper.dataset.parent = parentKey;
376
+ wrapper.dataset.depth = depth;
377
+
378
+ // Add indent lines
379
+ if (depth > 0) {
380
+ const indentLine = document.createElement('div');
381
+ indentLine.className = 'indent-line';
382
+ indentLine.style.left = `${depth * 20}px`;
383
+ wrapper.appendChild(indentLine);
384
+ }
385
+
386
+ // Create element content
387
+ const content = document.createElement('div');
388
+ content.className = 'flex items-start py-1';
389
+
390
+ // Key
391
+ if (key !== null && key !== 'root') {
392
+ const keyElement = document.createElement('span');
393
+ keyElement.className = 'json-key mr-2';
394
+ keyElement.textContent = `"${key}": `;
395
+ content.appendChild(keyElement);
396
+ }
397
+
398
+ // Value or children
399
+ if (typeof data === 'object' && data !== null) {
400
+ if (Array.isArray(data)) {
401
+ // Array
402
+ const bracket = document.createElement('span');
403
+ bracket.className = 'json-bracket';
404
+ bracket.textContent = '[';
405
+ content.appendChild(bracket);
406
+
407
+ wrapper.appendChild(content);
408
+ container.appendChild(wrapper);
409
+
410
+ // Render array items
411
+ data.forEach((item, index) => {
412
+ renderElement(container, item, depth + 1, index, key);
413
+ });
414
+
415
+ // Closing bracket
416
+ const closingWrapper = document.createElement('div');
417
+ closingWrapper.className = 'json-item relative pl-6';
418
+ closingWrapper.style.paddingLeft = `${depth * 20 + 8}px`;
419
+ closingWrapper.dataset.key = 'closing';
420
+ closingWrapper.dataset.parent = key;
421
+ closingWrapper.dataset.depth = depth;
422
+
423
+ if (depth > 0) {
424
+ const indentLine = document.createElement('div');
425
+ indentLine.className = 'indent-line';
426
+ indentLine.style.left = `${depth * 20}px`;
427
+ closingWrapper.appendChild(indentLine);
428
+ }
429
+
430
+ const closingContent = document.createElement('div');
431
+ closingContent.className = 'flex items-start py-1';
432
+ const closingBracket = document.createElement('span');
433
+ closingBracket.className = 'json-bracket';
434
+ closingBracket.textContent = ']';
435
+ closingContent.appendChild(closingBracket);
436
+ closingWrapper.appendChild(closingContent);
437
+ container.appendChild(closingWrapper);
438
+ } else {
439
+ // Object
440
+ const bracket = document.createElement('span');
441
+ bracket.className = 'json-bracket';
442
+ bracket.textContent = '{';
443
+ content.appendChild(bracket);
444
+
445
+ wrapper.appendChild(content);
446
+ container.appendChild(wrapper);
447
+
448
+ // Render object properties
449
+ Object.keys(data).forEach(propKey => {
450
+ renderElement(container, data[propKey], depth + 1, propKey, key);
451
+ });
452
+
453
+ // Closing bracket
454
+ const closingWrapper = document.createElement('div');
455
+ closingWrapper.className = 'json-item relative pl-6';
456
+ closingWrapper.style.paddingLeft = `${depth * 20 + 8}px`;
457
+ closingWrapper.dataset.key = 'closing';
458
+ closingWrapper.dataset.parent = key;
459
+ closingWrapper.dataset.depth = depth;
460
+
461
+ if (depth > 0) {
462
+ const indentLine = document.createElement('div');
463
+ indentLine.className = 'indent-line';
464
+ indentLine.style.left = `${depth * 20}px`;
465
+ closingWrapper.appendChild(indentLine);
466
+ }
467
+
468
+ const closingContent = document.createElement('div');
469
+ closingContent.className = 'flex items-start py-1';
470
+ const closingBracket = document.createElement('span');
471
+ closingBracket.className = 'json-bracket';
472
+ closingBracket.textContent = '}';
473
+ closingContent.appendChild(closingBracket);
474
+ closingWrapper.appendChild(closingContent);
475
+ container.appendChild(closingWrapper);
476
+ }
477
+ } else {
478
+ // Primitive value
479
+ const valueElement = document.createElement('span');
480
+ valueElement.className = 'json-value';
481
+
482
+ if (typeof data === 'string') {
483
+ valueElement.textContent = `"${data}"`;
484
+ } else if (typeof data === 'boolean') {
485
+ valueElement.textContent = data.toString();
486
+ } else {
487
+ valueElement.textContent = data;
488
+ }
489
+
490
+ content.appendChild(valueElement);
491
+ wrapper.appendChild(content);
492
+ container.appendChild(wrapper);
493
+ }
494
+
495
+ // Add event listeners for editing
496
+ if (wrapper.dataset.key !== 'closing') {
497
+ wrapper.addEventListener('dblclick', () => editElement(wrapper));
498
+ wrapper.addEventListener('click', () => selectElement(wrapper));
499
+ }
500
+ }
501
+
502
+ // Edit an element
503
+ function editElement(element) {
504
+ if (element.dataset.key === 'closing') return;
505
+
506
+ const keyElement = element.querySelector('.json-key');
507
+ const valueElement = element.querySelector('.json-value');
508
+
509
+ if (keyElement) {
510
+ const keyText = keyElement.textContent.replace(/"/g, '').replace(':', '').trim();
511
+ const input = document.createElement('input');
512
+ input.type = 'text';
513
+ input.className = 'editable bg-yellow-100 px-1';
514
+ input.value = keyText;
515
+ keyElement.replaceWith(input);
516
+ input.focus();
517
+
518
+ input.addEventListener('blur', () => {
519
+ const newKey = input.value;
520
+ updateKey(element, newKey);
521
+ input.replaceWith(keyElement);
522
+ keyElement.textContent = `"${newKey}": `;
523
+ });
524
+
525
+ input.addEventListener('keydown', (e) => {
526
+ if (e.key === 'Enter') {
527
+ input.blur();
528
+ }
529
+ });
530
+ }
531
+
532
+ if (valueElement) {
533
+ const valueText = valueElement.textContent.replace(/"/g, '');
534
+ const input = document.createElement('input');
535
+ input.type = 'text';
536
+ input.className = 'editable bg-yellow-100 px-1';
537
+ input.value = valueText;
538
+ valueElement.replaceWith(input);
539
+ input.focus();
540
+
541
+ input.addEventListener('blur', () => {
542
+ const newValue = parseValue(input.value);
543
+ updateValue(element, newValue);
544
+ input.replaceWith(valueElement);
545
+ valueElement.textContent = formatValue(newValue);
546
+ });
547
+
548
+ input.addEventListener('keydown', (e) => {
549
+ if (e.key === 'Enter') {
550
+ input.blur();
551
+ }
552
+ });
553
+ }
554
+ }
555
+
556
+ // Parse a value from string to appropriate type
557
+ function parseValue(value) {
558
+ if (value === 'true') return true;
559
+ if (value === 'false') return false;
560
+ if (value === 'null') return null;
561
+ if (!isNaN(value) && value.trim() !== '') return Number(value);
562
+ return value;
563
+ }
564
+
565
+ // Format a value for display
566
+ function formatValue(value) {
567
+ if (typeof value === 'string') return `"${value}"`;
568
+ if (typeof value === 'boolean') return value.toString();
569
+ if (value === null) return 'null';
570
+ return value;
571
+ }
572
+
573
+ // Update a key
574
+ function updateKey(element, newKey) {
575
+ const parentKey = element.dataset.parent;
576
+ const oldKey = element.dataset.key;
577
+
578
+ if (parentKey === 'root') {
579
+ const newData = {};
580
+ Object.keys(jsonData).forEach(key => {
581
+ if (key === oldKey) {
582
+ newData[newKey] = jsonData[key];
583
+ } else if (key !== newKey) {
584
+ newData[key] = jsonData[key];
585
+ }
586
+ });
587
+ jsonData = newData;
588
+ } else {
589
+ // Find parent in nested structure
590
+ const parent = findElementByKey(jsonData, parentKey);
591
+ if (parent && typeof parent === 'object') {
592
+ const newData = {};
593
+ Object.keys(parent).forEach(key => {
594
+ if (key === oldKey) {
595
+ newData[newKey] = parent[key];
596
+ } else if (key !== newKey) {
597
+ newData[key] = parent[key];
598
+ }
599
+ });
600
+ Object.keys(parent).forEach(key => delete parent[key]);
601
+ Object.assign(parent, newData);
602
+ }
603
+ }
604
+
605
+ element.dataset.key = newKey;
606
+ updateOutput();
607
+ saveToHistory();
608
+ }
609
+
610
+ // Update a value
611
+ function updateValue(element, newValue) {
612
+ const key = element.dataset.key;
613
+ const parentKey = element.dataset.parent;
614
+
615
+ if (parentKey === 'root') {
616
+ jsonData[key] = newValue;
617
+ } else {
618
+ const parent = findElementByKey(jsonData, parentKey);
619
+ if (parent && typeof parent === 'object') {
620
+ parent[key] = newValue;
621
+ }
622
+ }
623
+
624
+ updateOutput();
625
+ saveToHistory();
626
+ }
627
+
628
+ // Find an element by key in nested structure
629
+ function findElementByKey(obj, key) {
630
+ if (obj[key] !== undefined) return obj;
631
+
632
+ for (let prop in obj) {
633
+ if (typeof obj[prop] === 'object' && obj[prop] !== null) {
634
+ const result = findElementByKey(obj[prop], key);
635
+ if (result) return result;
636
+ }
637
+ }
638
+
639
+ return null;
640
+ }
641
+
642
+ // Select an element
643
+ function selectElement(element) {
644
+ if (selectedElement) {
645
+ selectedElement.classList.remove('selected');
646
+ }
647
+
648
+ element.classList.add('selected');
649
+ selectedElement = element;
650
+ }
651
+
652
+ // Update JSON output
653
+ function updateOutput() {
654
+ try {
655
+ jsonOutput.value = JSON.stringify(jsonData, null, 2);
656
+ jsonOutput.classList.remove('error-highlight');
657
+ } catch (e) {
658
+ jsonOutput.value = 'Invalid JSON structure';
659
+ jsonOutput.classList.add('error-highlight');
660
+ }
661
+ }
662
+
663
+ // Show notification
664
+ function showNotification(message) {
665
+ const notificationContent = notification.querySelector('p');
666
+ notificationContent.textContent = message;
667
+ notification.classList.add('show');
668
+
669
+ setTimeout(() => {
670
+ notification.classList.remove('show');
671
+ }, 3000);
672
+ }
673
+
674
+ // Save to history for undo/redo
675
+ function saveToHistory() {
676
+ // Remove future history if we're not at the end
677
+ if (historyIndex < history.length - 1) {
678
+ history = history.slice(0, historyIndex + 1);
679
+ }
680
+
681
+ history.push(JSON.parse(JSON.stringify(jsonData)));
682
+ historyIndex = history.length - 1;
683
+ }
684
+
685
+ // Undo action
686
+ function undo() {
687
+ if (historyIndex > 0) {
688
+ historyIndex--;
689
+ jsonData = JSON.parse(JSON.stringify(history[historyIndex]));
690
+ renderEditor();
691
+ updateOutput();
692
+ showNotification('Undo successful');
693
+ }
694
+ }
695
+
696
+ // Redo action
697
+ function redo() {
698
+ if (historyIndex < history.length - 1) {
699
+ historyIndex++;
700
+ jsonData = JSON.parse(JSON.stringify(history[historyIndex]));
701
+ renderEditor();
702
+ updateOutput();
703
+ showNotification('Redo successful');
704
+ }
705
+ }
706
+
707
+ // Event listeners for undo/redo
708
+ document.getElementById('undoBtn').addEventListener('click', undo);
709
+ document.getElementById('redoBtn').addEventListener('click', redo);
710
+
711
+ // Format JSON
712
+ document.getElementById('formatBtn').addEventListener('click', () => {
713
+ updateOutput();
714
+ showNotification('JSON formatted');
715
+ });
716
+
717
+ // Validate JSON
718
+ document.getElementById('validateBtn').addEventListener('click', () => {
719
+ try {
720
+ JSON.parse(jsonOutput.value);
721
+ showNotification('JSON is valid!');
722
+ } catch (e) {
723
+ showNotification('Invalid JSON: ' + e.message);
724
+ }
725
+ });
726
+ });
727
+ </script>
728
+ <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>
729
+ </html>